This commit is contained in:
EntenKoeniq
2021-08-18 11:47:59 +02:00
parent 56e42a757f
commit 3c05b11292
15 changed files with 610 additions and 260 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
**/bin
**/obj
**/packages
.vs/*
**/.vs

View File

@ -143,19 +143,22 @@ namespace CoopClient
if (!characterExist)
{
CreateCharacter(username);
if (!CreateCharacter(username))
{
return;
}
}
else if (LastSyncWasFull)
{
if (ModelHash != LastModelHash)
{
if (characterExist)
{
Character.Kill();
Character.Delete();
}
Character.Kill();
Character.Delete();
CreateCharacter(username);
if (!CreateCharacter(username))
{
return;
}
}
else if (Props != LastProps)
{
@ -232,30 +235,45 @@ namespace CoopClient
private void DisplayInVehicle()
{
if (MainVehicle == null || !MainVehicle.Exists() || MainVehicle.Model.Hash != VehicleModelHash)
try
{
List<Vehicle> vehs = World.GetNearbyVehicles(Character, 7f, new Model[] { VehicleModelHash }).OrderBy(v => (v.Position - Character.Position).Length()).Take(3).ToList();
bool vehFound = false;
foreach (Vehicle veh in vehs)
if (MainVehicle == null || !MainVehicle.Exists() || MainVehicle.Model.Hash != VehicleModelHash)
{
if (veh.IsSeatFree((VehicleSeat)VehicleSeatIndex))
bool vehFound = false;
List<Vehicle> vehs = World.GetNearbyVehicles(Character, 7f, new Model[] { VehicleModelHash }).OrderBy(v => (v.Position - Character.Position).Length()).Take(3).ToList();
foreach (Vehicle veh in vehs)
{
MainVehicle = veh;
vehFound = true;
break;
if (veh.IsSeatFree((VehicleSeat)VehicleSeatIndex))
{
MainVehicle = veh;
vehFound = true;
break;
}
}
if (!vehFound)
{
Model vehicleModel = Util.ModelRequest(VehicleModelHash);
if (vehicleModel == null)
{
GTA.UI.Notification.Show($"~r~Model ({VehicleModelHash}) cannot be loaded!");
return;
}
MainVehicle = World.CreateVehicle(vehicleModel, VehiclePosition);
MainVehicle.Quaternion = VehicleRotation;
}
}
if (!vehFound)
{
MainVehicle = World.CreateVehicle(new Model(VehicleModelHash), VehiclePosition);
MainVehicle.Quaternion = VehicleRotation;
}
}
catch (Exception e)
{
GTA.UI.Notification.Show("~r~" + e.Message);
return;
}
if (!Character.IsInVehicle() || (int)Character.SeatIndex != VehicleSeatIndex || Character.CurrentVehicle.Model.Hash != VehicleModelHash)
if (!Character.IsInVehicle() || (int)Character.SeatIndex != VehicleSeatIndex || Character.CurrentVehicle?.Model.Hash != VehicleModelHash)
{
if (VehicleSeatIndex == -1 &&
Game.Player.Character.IsInVehicle() &&
@ -573,12 +591,20 @@ namespace CoopClient
}
}
private void CreateCharacter(string username)
private bool CreateCharacter(string username)
{
LastModelHash = ModelHash;
LastProps = Props;
Character = World.CreatePed(new Model(ModelHash), Position, Rotation.Z);
Model characterModel = Util.ModelRequest(ModelHash);
if (characterModel == null)
{
GTA.UI.Notification.Show($"~r~Model ({ModelHash}) cannot be loaded!");
return false;
}
Character = World.CreatePed(characterModel, Position, Rotation.Z);
Character.RelationshipGroup = Main.RelationshipGroup;
if (IsInVehicle)
{
@ -606,6 +632,8 @@ namespace CoopClient
{
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, Character.Handle, prop.Key, prop.Value, 0, 0);
}
return true;
}
private bool LastMoving;

View File

@ -2,6 +2,7 @@
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Drawing;
using CoopClient.Entities;
using CoopClient.Menus;
@ -20,16 +21,18 @@ namespace CoopClient
public static readonly string CurrentModVersion = "V0_5_0";
public static bool ShareNpcsWithPlayers = false;
public static bool DisableTraffic = false;
public static bool NpcsAllowed = false;
private static bool IsGoingToCar = false;
public static Settings MainSettings = Util.ReadSettings();
public static MenusMain MainMenu = new MenusMain();
public static Chat MainChat = new Chat();
public static Networking MainNetworking = new Networking();
public static MenusMain MainMenu = new MenusMain();
public static Chat MainChat = new Chat();
public static long LocalPlayerID = 0;
public static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
public static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
@ -73,6 +76,15 @@ namespace CoopClient
return;
}
#if DEBUG
if (MainNetworking.ShowNetworkInfo)
{
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0), $"L: {MainNetworking.Latency * 1000:N0}ms", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {MainNetworking.BytesReceived} bytes", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {MainNetworking.BytesSend} bytes", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
}
#endif
MainChat.Tick();
// Display all players
@ -81,10 +93,12 @@ namespace CoopClient
player.Value.DisplayLocally(player.Value.Username);
}
#if DEBUG
if (UseDebug)
{
Debug();
}
#endif
if ((Environment.TickCount - LastDataSend) >= (1000 / 60))
{
@ -156,25 +170,13 @@ namespace CoopClient
private void OnAbort(object sender, EventArgs e)
{
foreach (KeyValuePair<long, EntitiesPlayer> player in Players)
{
player.Value.Character?.AttachedBlip?.Delete();
player.Value.Character?.CurrentVehicle?.Delete();
player.Value.Character?.Kill();
player.Value.Character?.Delete();
player.Value.PedBlip?.Delete();
}
foreach (KeyValuePair<long, EntitiesNpc> Npc in Npcs)
{
Npc.Value.Character?.CurrentVehicle?.Delete();
Npc.Value.Character?.Kill();
Npc.Value.Character?.Delete();
}
CleanUp();
}
public static void CleanUp()
{
MainChat.Clear();
foreach (KeyValuePair<long, EntitiesPlayer> player in Players)
{
player.Value.Character?.AttachedBlip?.Delete();
@ -193,33 +195,15 @@ namespace CoopClient
}
Npcs.Clear();
foreach (Ped entity in World.GetAllPeds())
foreach (Ped entity in World.GetAllPeds().Where(p => p.Handle != Game.Player.Character.Handle))
{
if (entity.Handle != Game.Player.Character.Handle)
{
entity.Kill();
entity.Delete();
}
entity.Kill();
entity.Delete();
}
if (!Game.Player.Character.IsInVehicle())
foreach (Vehicle veh in World.GetAllVehicles().Where(v => v.Handle != Game.Player.Character.Handle))
{
foreach (Vehicle vehicle in World.GetAllVehicles())
{
vehicle.Delete();
}
}
else
{
int? playerVehicleHandle = Game.Player.Character.CurrentVehicle?.Handle;
foreach (Vehicle vehicle in World.GetAllVehicles())
{
if (playerVehicleHandle != vehicle.Handle)
{
vehicle.Delete();
}
}
veh.Delete();
}
}

View File

@ -9,26 +9,32 @@ namespace CoopClient.Menus.Sub
UseMouse = false,
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
private readonly NativeCheckboxItem DisableTraffic = new NativeCheckboxItem("Disable Traffic", Main.DisableTraffic);
private readonly NativeCheckboxItem ShareNpcsItem = new NativeCheckboxItem("Share Npcs", Main.ShareNpcsWithPlayers) { Enabled = false };
private readonly NativeSliderItem StreamedNpcsItem = new NativeSliderItem(string.Format("Streamed Npcs ({0})", Main.MainSettings.StreamedNpc), 20, Main.MainSettings.StreamedNpc);
private readonly NativeCheckboxItem FlipMenuItem = new NativeCheckboxItem("Flip menu", Main.MainSettings.FlipMenu);
private readonly NativeCheckboxItem UseDebugItem = new NativeCheckboxItem("Debug", Main.UseDebug);
private readonly NativeCheckboxItem ShowNetworkInfo = new NativeCheckboxItem("Show Network Info", Main.MainNetworking.ShowNetworkInfo);
public Settings()
{
DisableTraffic.CheckboxChanged += DisableTrafficCheckboxChanged;
ShareNpcsItem.CheckboxChanged += (item, check) => { Main.ShareNpcsWithPlayers = ShareNpcsItem.Checked; };
StreamedNpcsItem.ValueChanged += StreamedNpcsValueChanged;
FlipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged;
#if DEBUG
UseDebugItem.CheckboxChanged += UseDebugCheckboxChanged;
ShowNetworkInfo.CheckboxChanged += ShowNetworkInfoCheckboxChanged;
#endif
MainMenu.Add(DisableTraffic);
MainMenu.Add(ShareNpcsItem);
MainMenu.Add(StreamedNpcsItem);
MainMenu.Add(FlipMenuItem);
#if DEBUG
MainMenu.Add(UseDebugItem);
MainMenu.Add(ShowNetworkInfo);
#endif
}
@ -65,5 +71,35 @@ namespace CoopClient.Menus.Sub
Main.Players.Remove(0);
}
}
public void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
{
Main.MainNetworking.ShowNetworkInfo = ShowNetworkInfo.Checked;
if (!Main.MainNetworking.ShowNetworkInfo)
{
Main.MainNetworking.BytesReceived = 0;
Main.MainNetworking.BytesSend = 0;
}
}
public void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{
Main.DisableTraffic = DisableTraffic.Checked;
if (DisableTraffic.Checked)
{
if (ShareNpcsItem.Checked)
{
ShareNpcsItem.Checked = false;
}
ShareNpcsItem.Enabled = false;
}
else if (Main.NpcsAllowed && !ShareNpcsItem.Enabled)
{
ShareNpcsItem.Enabled = true;
}
}
}
}

View File

@ -14,6 +14,11 @@ namespace CoopClient
public NetClient Client;
public float Latency;
public bool ShowNetworkInfo = false;
public int BytesReceived = 0;
public int BytesSend = 0;
public void DisConnectFromServer(string address)
{
if (IsOnServer())
@ -72,6 +77,8 @@ namespace CoopClient
while ((message = Client.ReadMessage()) != null)
{
BytesReceived += message.LengthBytes;
switch (message.MessageType)
{
case NetIncomingMessageType.StatusChanged:
@ -125,7 +132,7 @@ namespace CoopClient
Main.MainMenu.MainMenu.Items[2].Enabled = true;
Main.MainMenu.MainMenu.Items[2].Title = "Disconnect";
Main.MainMenu.SubSettings.MainMenu.Items[0].Enabled = Main.NpcsAllowed;
Main.MainMenu.SubSettings.MainMenu.Items[1].Enabled = !Main.DisableTraffic && Main.NpcsAllowed;
Main.MainMenu.MainMenu.Visible = false;
Main.MainMenu.MenuPool.RefreshAll();
@ -144,15 +151,13 @@ namespace CoopClient
Main.MainChat.Focused = false;
}
Main.MainChat.Clear();
Main.CleanUp();
Main.MainMenu.MainMenu.Items[0].Enabled = true;
Main.MainMenu.MainMenu.Items[1].Enabled = true;
Main.MainMenu.MainMenu.Items[2].Enabled = true;
Main.MainMenu.MainMenu.Items[2].Title = "Connect";
Main.MainMenu.SubSettings.MainMenu.Items[0].Enabled = false;
Main.MainMenu.SubSettings.MainMenu.Items[1].Enabled = false;
Main.MainMenu.MenuPool.RefreshAll();
break;
@ -654,6 +659,13 @@ namespace CoopClient
Client.SendMessage(outgoingMessage, messageType);
Client.FlushSendQueue();
#if DEBUG
if (ShowNetworkInfo)
{
BytesSend += outgoingMessage.LengthBytes;
}
#endif
}
public void SendNpcData(Ped npc)
@ -710,6 +722,13 @@ namespace CoopClient
Client.SendMessage(outgoingMessage, NetDeliveryMethod.Unreliable);
Client.FlushSendQueue();
#if DEBUG
if (ShowNetworkInfo)
{
BytesSend += outgoingMessage.LengthBytes;
}
#endif
}
public void SendChatMessage(string message)
@ -722,6 +741,13 @@ namespace CoopClient
}.PacketToNetOutGoingMessage(outgoingMessage);
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableOrdered);
Client.FlushSendQueue();
#if DEBUG
if (ShowNetworkInfo)
{
BytesSend += outgoingMessage.LengthBytes;
}
#endif
}
#endregion
}

View File

@ -79,6 +79,26 @@ namespace CoopClient
}
#endregion
public static Model ModelRequest(int hash)
{
Model model = new Model(hash);
short counter = 0;
while (counter++ < 1000)
{
model.Request();
Script.Yield();
if (model.IsLoaded)
{
return model;
}
}
return null;
}
public static bool IsBetween<T>(this T item, T start, T end)
{
return Comparer<T>.Default.Compare(item, start) >= 0 && Comparer<T>.Default.Compare(item, end) <= 0;
@ -438,7 +458,7 @@ namespace CoopClient
dir.Normalize();
RaycastResult raycastResults = World.Raycast(source3D + dir * raycastFromDist,
source3D + dir * raycastToDist,
(IntersectFlags)(1 | 16 | 256 | 2 | 4 | 8), // | peds + vehicles
IntersectFlags.Everything,
ignoreEntity);
if (raycastResults.DidHit)

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using GTA;
using GTA.Native;
@ -7,10 +8,19 @@ namespace CoopClient
{
public class WorldThread : Script
{
private static bool LastDisableTraffic = false;
public WorldThread()
{
Tick += OnTick;
Interval = 1000 / 60;
Aborted += (sender, e) =>
{
if (LastDisableTraffic)
{
Traffic(true);
}
};
}
public static void OnTick(object sender, EventArgs e)
@ -24,6 +34,75 @@ namespace CoopClient
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, Game.Player.Character.Handle, true, false);
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED, Game.Player.Character.Handle, true);
if (Main.DisableTraffic)
{
if (!LastDisableTraffic)
{
Traffic(false);
}
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SET_RANDOM_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SET_PARKED_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call((Hash)0x2F9A292AD0A3BD89);
Function.Call((Hash)0x5F3B7749C112D552);
}
else if (LastDisableTraffic)
{
Traffic(true);
}
LastDisableTraffic = Main.DisableTraffic;
}
private static void Traffic(bool enable)
{
if (enable)
{
Function.Call(Hash.REMOVE_SCENARIO_BLOCKING_AREAS);
Function.Call(Hash.SET_CREATE_RANDOM_COPS, true);
Function.Call(Hash.SET_RANDOM_TRAINS, true);
Function.Call(Hash.SET_RANDOM_BOATS, true);
Function.Call(Hash.SET_GARBAGE_TRUCKS, true);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3); // 0 - 3
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3); // 0 - 3
Function.Call(Hash.SET_ALL_VEHICLE_GENERATORS_ACTIVE);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, -1);
Function.Call((Hash)0xF796359A959DF65D, true); // Display distant vehicles
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, false);
}
else
{
Function.Call(Hash.ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f, 1000.0f, 0, 1, 1, 1);
Function.Call(Hash.SET_CREATE_RANDOM_COPS, false);
Function.Call(Hash.SET_RANDOM_TRAINS, false);
Function.Call(Hash.SET_RANDOM_BOATS, false);
Function.Call(Hash.SET_GARBAGE_TRUCKS, false);
Function.Call(Hash.DELETE_ALL_TRAINS);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
Function.Call((Hash)0xF796359A959DF65D, false); //Display distant vehicles
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true);
foreach (Ped ped in World.GetAllPeds().Where(p => p.RelationshipGroup != "SYNCPED"))
{
ped.CurrentVehicle?.Delete();
ped.Kill();
ped.Delete();
}
foreach (Vehicle veh in World.GetAllVehicles().Where(v => v.IsSeatFree(VehicleSeat.Driver) && v.PassengerCount == 0))
{
veh.Delete();
}
}
}
}
}

View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31605.320
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FirstGameMode", "FirstGameMode\FirstGameMode.csproj", "{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3C329584-BE48-469B-85D8-FD24F47BD033}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,46 @@
using System.Linq;
using CoopServer;
namespace FirstGameMode
{
class Commands
{
[Command("hello")]
public static void HelloCommand(CommandContext ctx)
{
ServerScript.SendChatMessageToPlayer(ctx.Player.Username, "Hello " + ctx.Player.Username + " :)");
}
[Command("inrange")]
public static void InRangeCommand(CommandContext ctx)
{
if (ctx.Player.Ped.IsInRangeOf(new LVector3(0f, 0f, 75f), 7f))
{
ServerScript.SendChatMessageToPlayer(ctx.Player.Username, "You are in range! :)");
}
else
{
ServerScript.SendChatMessageToPlayer(ctx.Player.Username, "You are not in range! :(");
}
}
[Command("online")]
public static void OnlineCommand(CommandContext ctx)
{
ServerScript.SendChatMessageToPlayer(ctx.Player.Username, ServerScript.GetAllPlayersCount() + " player online!");
}
[Command("kick")]
public static void KickCommand(CommandContext ctx)
{
if (ctx.Args.Length < 2)
{
ServerScript.SendChatMessageToPlayer(ctx.Player.Username, "Please use \"/kick <USERNAME> <REASON>\"");
return;
}
ServerScript.KickPlayerByUsername(ctx.Args[0], ctx.Args.Skip(1).ToArray());
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="CoopServer">
<HintPath>..\..\Server\bin\Debug\net5.0\CoopServer.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,47 @@
using CoopServer;
using CoopServer.Entities;
using System.Timers;
namespace FirstGameMode
{
public class Main : ServerScript
{
private static readonly Timer RunningSinceTimer = new() { Interval = 1000 };
private static int RunningSince = 0;
public override void Start()
{
RunningSinceTimer.Start();
RunningSinceTimer.Elapsed += new ElapsedEventHandler((sender, e) => RunningSince += 1);
RegisterCommand("running", RunningCommand);
RegisterCommands<Commands>();
}
public static void RunningCommand(CommandContext ctx)
{
SendChatMessageToPlayer(ctx.Player.Username, "Server has been running for: " + RunningSince + " seconds!");
}
public override void OnPlayerConnect(EntitiesPlayer client)
{
SendChatMessageToAll("Player " + client.Username + " connected!");
}
public override void OnPlayerDisconnect(EntitiesPlayer player, string reason)
{
SendChatMessageToAll(player.Username + " left the server, reason: " + reason);
}
public override bool OnChatMessage(string username, string message)
{
if (message.StartsWith("EASTEREGG"))
{
SendChatMessageToPlayer(username, "You found the EASTEREGG! *-*");
return true;
}
return false;
}
}
}

View File

@ -32,6 +32,7 @@ namespace CoopServer
public static readonly Dictionary<long, EntitiesPlayer> Players = new();
private static ServerScript GameMode;
public static readonly Dictionary<Command, Action<CommandContext>> Commands = new Dictionary<Command, Action<CommandContext>>();
public Server()
{
@ -127,12 +128,7 @@ namespace CoopServer
#endregion
}
if (MainSettings.DefaultGameMode)
{
GameMode = new DefaultScript();
GameMode.Start();
}
else if (!string.IsNullOrEmpty(MainSettings.GameMode))
if (!string.IsNullOrEmpty(MainSettings.GameMode))
{
try
{
@ -381,27 +377,6 @@ namespace CoopServer
}
}
// Return a list of all connections but not the local connection
private static List<NetConnection> FilterAllLocal(NetConnection local)
{
return new(MainNetServer.Connections.Where(e => e != local));
}
private static List<NetConnection> FilterAllLocal(long local)
{
return new(MainNetServer.Connections.Where(e => e.RemoteUniqueIdentifier != local));
}
// Return a list of players within range of ...
private static List<NetConnection> GetAllInRange(LVector3 position, float range)
{
return new(MainNetServer.Connections.FindAll(e => Players[e.RemoteUniqueIdentifier].Ped.IsInRangeOf(position, range)));
}
// Return a list of players within range of ... but not the local one
private static List<NetConnection> GetAllInRange(LVector3 position, float range, NetConnection local)
{
return new(MainNetServer.Connections.Where(e => e != local && Players[e.RemoteUniqueIdentifier].Ped.IsInRangeOf(position, range)));
}
#region -- PLAYER --
// Before we approve the connection, we must shake hands
private void GetHandshake(NetConnection local, HandshakePacket packet)
@ -504,7 +479,7 @@ namespace CoopServer
GameMode.OnPlayerConnect(Players[packet.Player]);
}
List<NetConnection> playerList = FilterAllLocal(local);
List<NetConnection> playerList = Util.FilterAllLocal(local);
if (playerList.Count == 0)
{
return;
@ -546,7 +521,7 @@ namespace CoopServer
GameMode.OnPlayerDisconnect(Players[packet.Player], reason);
}
List<NetConnection> playerList = FilterAllLocal(packet.Player);
List<NetConnection> playerList = Util.FilterAllLocal(packet.Player);
if (playerList.Count != 0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
@ -563,7 +538,7 @@ namespace CoopServer
player.Ped.Position = packet.Extra.Position;
List<NetConnection> playerList = FilterAllLocal(packet.Extra.Player);
List<NetConnection> playerList = Util.FilterAllLocal(packet.Extra.Player);
if (playerList.Count == 0)
{
return;
@ -585,7 +560,7 @@ namespace CoopServer
player.Ped.Position = packet.Extra.Position;
List<NetConnection> playerList = FilterAllLocal(packet.Extra.Player);
List<NetConnection> playerList = Util.FilterAllLocal(packet.Extra.Player);
if (playerList.Count == 0)
{
return;
@ -607,7 +582,7 @@ namespace CoopServer
player.Ped.Position = packet.Extra.Position;
List<NetConnection> playerList = FilterAllLocal(packet.Extra.Player);
List<NetConnection> playerList = Util.FilterAllLocal(packet.Extra.Player);
if (playerList.Count == 0)
{
return;
@ -629,7 +604,7 @@ namespace CoopServer
player.Ped.Position = packet.Extra.Position;
List<NetConnection> playerList = FilterAllLocal(packet.Extra.Player);
List<NetConnection> playerList = Util.FilterAllLocal(packet.Extra.Player);
if (playerList.Count == 0)
{
return;
@ -648,25 +623,62 @@ namespace CoopServer
// Send a message to targets or all players
private static void SendChatMessage(ChatMessagePacket packet, List<NetConnection> targets = null)
{
if (GameMode != null && GameMode.OnChatMessage(packet.Username, packet.Message))
NetOutgoingMessage outgoingMessage;
if (GameMode != null)
{
return;
if (packet.Message.StartsWith("/"))
{
string[] cmdArgs = packet.Message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1);
if (Commands.Any(x => x.Key.Name == cmdName))
{
CommandContext ctx = new()
{
Player = Players.First(x => x.Value.Username == packet.Username).Value,
Args = cmdArgs.Skip(1).ToArray()
};
KeyValuePair<Command, Action<CommandContext>> command = Commands.First(x => x.Key.Name == cmdName);
command.Value.Invoke(ctx);
}
else
{
string username = packet.Username;
packet = new()
{
Username = "Server",
Message = "Command not found!"
};
outgoingMessage = MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections.Find(con => con.RemoteUniqueIdentifier == Players.First(x => x.Value.Username == username).Key), NetDeliveryMethod.ReliableOrdered, 0);
}
return;
}
else if (GameMode.OnChatMessage(packet.Username, packet.Message))
{
return;
}
}
packet.Message = packet.Message.Replace("~", "");
Logging.Info(packet.Username + ": " + packet.Message);
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage = MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, targets ?? MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
Logging.Info(packet.Username + ": " + packet.Message);
}
#endregion
#region -- NPC --
private static void FullSyncNpc(NetConnection local, FullSyncNpcPacket packet)
{
List<NetConnection> playerList = GetAllInRange(packet.Position, 300f, local);
List<NetConnection> playerList = Util.GetAllInRange(packet.Position, 300f, local);
if (playerList.Count == 0)
{
return;
@ -679,7 +691,7 @@ namespace CoopServer
private static void FullSyncNpcVeh(NetConnection local, FullSyncNpcVehPacket packet)
{
List<NetConnection> playerList = GetAllInRange(packet.Position, 300f, local);
List<NetConnection> playerList = Util.GetAllInRange(packet.Position, 300f, local);
if (playerList.Count == 0)
{
return;
@ -690,5 +702,29 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, playerList, NetDeliveryMethod.UnreliableSequenced, 0);
}
#endregion
public static void RegisterCommand(string name, Action<CommandContext> callback)
{
Command command = new() { Name = name };
if (Commands.ContainsKey(command))
{
throw new Exception("Command \"" + command.Name + "\" was already been registered!");
}
Commands.Add(command, callback);
}
public static void RegisterCommands<T>()
{
IEnumerable<MethodInfo> commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(CommandAttribute), false).Any());
foreach (MethodInfo method in commands)
{
CommandAttribute attribute = method.GetCustomAttribute<CommandAttribute>(true);
RegisterCommand(attribute.Name, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
}
}
}
}

View File

@ -5,100 +5,9 @@ using Lidgren.Network;
namespace CoopServer
{
class DefaultScript : ServerScript
{
private List<string> Admins = new();
private string AdminPassword = "test123";
public override void OnPlayerConnect(Entities.EntitiesPlayer client)
{
SendChatMessageToAll("Say hello to " + client.Username);
}
public override void OnPlayerDisconnect(Entities.EntitiesPlayer player, string reason)
{
Logging.Info(player.Username + " left the server, reason: " + reason);
if (Admins.Contains(player.Username))
{
Admins.Remove(player.Username);
}
}
public override bool OnChatMessage(string username, string message)
{
if (!message.StartsWith("/"))
{
return false;
}
string[] messageSplitted = message.Split(" ");
int messageSplittedLength = messageSplitted.Length;
if (messageSplittedLength == 0)
{
return true;
}
if (!Admins.Contains(username))
{
if (messageSplitted[0] != "/rcon")
{
SendChatMessageToPlayer(username, "Please login with \"/rcon <PASSWORD>\"!");
return true;
}
if (messageSplitted.Length < 2)
{
SendChatMessageToPlayer(username, "Password missing!");
return true;
}
if (messageSplitted[1] != AdminPassword)
{
SendChatMessageToPlayer(username, "Wrong password!");
Logging.Warning("Player [" + username + "] tried to login rcon with [" + messageSplitted[1] + "]");
return true;
}
Admins.Add(username);
SendChatMessageToPlayer(username, "Login successfully!");
Logging.Info("Login successfully! [RCON][" + username + "]");
return true;
}
if (messageSplitted[0] == "/kick")
{
if (messageSplittedLength < 3)
{
SendChatMessageToPlayer(username, "Please use \"/kick <USERNAME> <REASON>\"");
return true;
}
try
{
KickPlayerByUsername(messageSplitted[1], messageSplittedLength >= 3 ? messageSplitted[2] : "Kicked by " + username + "!");
SendChatMessageToPlayer(username, "Player [" + messageSplitted[1] + "] kicked!");
}
catch (Exception e)
{
SendChatMessageToPlayer(username, e.Message);
}
return true;
}
SendChatMessageToPlayer(username, "Command \"" + messageSplitted[0] + "\" not found!");
return true;
}
}
public class ServerScript
{
public virtual void Start()
{
Logging.Info("Gamemode loaded successfully!");
}
public virtual void Start() { }
public virtual void OnPlayerConnect(Entities.EntitiesPlayer player)
{
@ -110,78 +19,139 @@ namespace CoopServer
Logging.Info(player.Username + " left the server, reason: " + reason);
}
public virtual bool OnChatMessage(string username, string message) { return false; }
protected static List<string> GetAllConnections()
public virtual bool OnChatMessage(string username, string message)
{
List<string> result = new();
return false;
}
public static List<long> GetAllConnections()
{
List<long> result = new();
lock (Server.MainNetServer.Connections)
{
Server.MainNetServer.Connections.ForEach(con => result.Add(NetUtility.ToHexString(con.RemoteUniqueIdentifier)));
Server.MainNetServer.Connections.ForEach(x => result.Add(x.RemoteUniqueIdentifier));
}
return result;
}
protected static int GetAllPlayersCount() { lock (Server.Players) return Server.Players.Count; }
protected static Dictionary<long, Entities.EntitiesPlayer> GetAllPlayers() { lock (Server.Players) return Server.Players; }
protected static void KickPlayerByUsername(string username, string reason)
public static int GetAllPlayersCount()
{
lock (Server.Players)
{
foreach (KeyValuePair<long, Entities.EntitiesPlayer> player in Server.Players)
{
if (player.Value.Username == username)
{
Server.MainNetServer.Connections.Find(e => e.RemoteUniqueIdentifier == player.Key).Disconnect(reason);
return;
}
}
return Server.Players.Count;
}
throw new Exception("Player [" + username + "] not found!");
}
protected static void SendChatMessageToAll(string message, string username = "Server")
{
ChatMessagePacket packet = new()
{
Username = username,
Message = message
};
Logging.Info(username + ": " + packet.Message);
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
}
protected static void SendChatMessageToPlayer(string username, string message, string from = "Server")
public static Dictionary<long, Entities.EntitiesPlayer> GetAllPlayers()
{
lock (Server.Players)
{
foreach (KeyValuePair<long, Entities.EntitiesPlayer> player in Server.Players)
{
if (player.Value.Username == username)
{
ChatMessagePacket packet = new()
{
Username = from,
Message = message
};
Logging.Info(from + ": " + packet.Message);
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections.Find(con => con.RemoteUniqueIdentifier == player.Key), NetDeliveryMethod.ReliableOrdered, 0);
return;
}
}
return Server.Players;
}
}
public static void KickPlayerByUsername(string username, string[] reason)
{
lock (Server.MainNetServer.Connections)
{
NetConnection userConnection = Util.GetConnectionByUsername(username);
if (userConnection == null)
{
Logging.Warning("[ServerScript->KickPlayerByUsername(\"" + username + "\", \"" + string.Join(" ", reason) + "\")]: User not found!");
return;
}
userConnection.Disconnect(string.Join(" ", reason));
}
}
public static void SendChatMessageToAll(string message, string username = "Server")
{
List<NetConnection> connections = Server.MainNetServer.Connections;
if (connections.Count != 0)
{
ChatMessagePacket packet = new()
{
Username = username,
Message = message
};
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
}
Logging.Info(username + ": " + message);
}
public static void SendChatMessageToPlayer(string username, string message, string from = "Server")
{
lock (Server.MainNetServer.Connections)
{
NetConnection userConnection = Util.GetConnectionByUsername(username);
if (userConnection == null)
{
Logging.Warning("[ServerScript->SendChatMessageToPlayer(\"" + username + "\", \"" + message + "\", \"" + from + "\")]: User not found!");
return;
}
ChatMessagePacket packet = new()
{
Username = from,
Message = message
};
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
}
Logging.Info(from + ": " + message);
}
public static void RegisterCommand(string name, Action<CommandContext> callback)
{
Server.RegisterCommand(name, callback);
}
public static void RegisterCommands<T>()
{
Server.RegisterCommands<T>();
}
}
public class Command
{
public string Name { get; set; }
}
[AttributeUsage(AttributeTargets.Method)]
public class CommandAttribute : Attribute
{
/// <summary>
/// Sets name of the command
/// </summary>
public string Name { get; set; }
public CommandAttribute(string name)
{
Name = name;
}
}
public class CommandContext
{
/// <summary>
/// Gets the client which executed the command
/// </summary>
public Entities.EntitiesPlayer Player { get; internal set; }
/// <summary>
/// Gets the chatdata associated with the command
/// </summary>
public string[] Args { get; internal set; }
}
}

View File

@ -6,7 +6,6 @@
public int MaxPlayers { get; set; } = 16;
public string ServerName { get; set; } = "GTACoop:R server";
public string WelcomeMessage { get; set; } = "Welcome on this server :)";
public bool DefaultGameMode { get; set; } = true;
public string GameMode { get; set; } = "";
public bool Allowlist { get; set; } = false;
public bool NpcsAllowed { get; set; } = true;

View File

@ -1,11 +1,52 @@
using System;
using System.IO;
using System.Xml.Serialization;
using System.Linq;
using System.Collections.Generic;
using Lidgren.Network;
namespace CoopServer
{
class Util
{
public static NetConnection GetConnectionByUsername(string username)
{
long? userID = GetIdByUsername(username);
if (userID == null || !Server.MainNetServer.Connections.Any(x => x.RemoteUniqueIdentifier == userID))
{
return null;
}
return Server.MainNetServer.Connections.First(x => x.RemoteUniqueIdentifier == userID);
}
public static long? GetIdByUsername(string username)
{
return Server.Players.Any(x => x.Value.Username == username) ? Server.Players.First(x => x.Value.Username == username).Key : null;
}
// Return a list of all connections but not the local connection
public static List<NetConnection> FilterAllLocal(NetConnection local)
{
return new(Server.MainNetServer.Connections.Where(e => e != local));
}
public static List<NetConnection> FilterAllLocal(long local)
{
return new(Server.MainNetServer.Connections.Where(e => e.RemoteUniqueIdentifier != local));
}
// Return a list of players within range of ...
public static List<NetConnection> GetAllInRange(LVector3 position, float range)
{
return new(Server.MainNetServer.Connections.FindAll(e => Server.Players[e.RemoteUniqueIdentifier].Ped.IsInRangeOf(position, range)));
}
// Return a list of players within range of ... but not the local one
public static List<NetConnection> GetAllInRange(LVector3 position, float range, NetConnection local)
{
return new(Server.MainNetServer.Connections.Where(e => e != local && Server.Players[e.RemoteUniqueIdentifier].Ped.IsInRangeOf(position, range)));
}
public static T Read<T>(string file) where T : new()
{
XmlSerializer ser = new(typeof(T));