Fixed three players issue, some work on API and resources management.

This commit is contained in:
Sardelka 2022-06-06 17:37:11 +08:00
parent 0914afc4ed
commit a1dda2ffa4
28 changed files with 559 additions and 653 deletions

View File

@ -1,192 +0,0 @@
#undef DEBUG
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using RageCoop.Core;
namespace RageCoop.Client
{
/// <summary>
/// ?
/// </summary>
public static class COOPAPI
{
#region DELEGATES
/// <summary>
/// ?
/// </summary>
/// <param name="connected"></param>
/// <param name="from">The player's id</param>
/// <param name="reason"></param>
public delegate void ConnectEvent(bool connected, int from, string reason = null);
/// <summary>
/// ?
/// </summary>
/// <param name="from"></param>
/// <param name="message">The Lidgren-Network net handle</param>
/// <param name="args"></param>
public delegate void ChatMessage(string from, string message, CancelEventArgs args);
/// <summary>
/// ?
/// </summary>
/// <param name="from">The Lidgren-Network net handle</param>
/// <param name="mod"></param>
/// <param name="customID"></param>
/// <param name="bytes"></param>
public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes);
#endregion
#region EVENTS
/// <summary>
/// ?
/// </summary>
public static event ConnectEvent OnConnection;
/// <summary>
/// ?
/// </summary>
public static event ChatMessage OnChatMessage;
public static void Connected()
{
OnConnection?.Invoke(true, GetPlayerID());
}
public static void Disconnected(string reason)
{
OnConnection?.Invoke(false, GetPlayerID(), reason);
}
public static void Connected(int playerID)
{
OnConnection?.Invoke(true, playerID);
}
public static void Disconnected(int playerID)
{
OnConnection?.Invoke(false, playerID);
}
public static bool ChatMessageReceived(string from, string message)
{
CancelEventArgs args = new CancelEventArgs(false);
OnChatMessage?.Invoke(from, message, args);
return args.Cancel;
}
#endregion
/// <summary>
/// Send a local chat message to this player
/// </summary>
/// <param name="from">Username of the player who sent this message</param>
/// <param name="message">The player's message</param>
public static void LocalChatMessage(string from, string message)
{
Main.MainChat.AddMessage(from, message);
}
/// <summary>
/// Connect to any server
/// </summary>
/// <param name="serverAddress">The server address to connect. Example: 127.0.0.1:4499</param>
public static void Connect(string serverAddress)
{
Networking.ToggleConnection(serverAddress);
}
/// <summary>
/// ?
/// </summary>
public static void Disconnect()
{
Networking.ToggleConnection(null);
}
/// <summary>
/// Check if the player is already on a server
/// </summary>
public static bool IsOnServer()
{
return Networking.IsOnServer;
}
/// <summary>
/// Get the local player's ID
/// </summary>
/// <returns>PlayerID</returns>
public static int GetPlayerID()
{
return Main.LocalPlayerID;
}
/// <summary>
/// Check if a RAGECOOP menu is visible
/// </summary>
public static bool IsMenuVisible()
{
#if NON_INTERACTIVE
return false;
#else
return Menus.CoopMenu.MenuPool.AreAnyVisible;
#endif
}
/// <summary>
/// Check if the RAGECOOP chat is visible
/// </summary>
public static bool IsChatFocused()
{
return Main.MainChat.Focused;
}
/// <summary>
/// Check if the RAGECOOP list of players is visible
/// </summary>
public static bool IsPlayerListVisible()
{
return Util.GetTickCount64() - PlayerList.Pressed < 5000;
}
/// <summary>
/// Get the version of RAGECOOP
/// </summary>
public static string GetCurrentVersion()
{
return Main.CurrentVersion;
}
/// <summary>
/// Get that player's local username
/// </summary>
public static string GetUsername()
{
return Main.Settings.Username;
}
/// <summary>
/// Set a new username for this player
/// </summary>
/// <param name="username">The new username</param>
/// <returns>false if the player already joined a server or the username is null or empty otherwise true</returns>
public static bool SetUsername(string username)
{
if (IsOnServer() || string.IsNullOrEmpty(username))
{
return false;
}
Main.Settings.Username = username;
return true;
}
/// <summary>
/// Get or set the client's settings.
/// </summary>
/// <returns>The client's settings, you should NEVER change settings without notifying the player.</returns>
public static Settings Settings()
{
return Main.Settings;
}
}
}

View File

@ -159,7 +159,7 @@ namespace RageCoop.Client
MainChat.Tick();
PlayerList.Tick();
if (Settings.DisableAutoRespawn)
if (!API.Config.EnableAutoRespawn)
{
Function.Call(Hash.PAUSE_DEATH_ARREST_RESTART, true);
Function.Call(Hash.FORCE_GAME_STATE_PLAYING);

View File

@ -7,11 +7,11 @@ namespace RageCoop.Client
internal static class DownloadManager
{
private static readonly List<DownloadFile> _downloadFiles = new List<DownloadFile>();
private static readonly Dictionary<byte, FileStream> _streams = new Dictionary<byte, FileStream>();
private static readonly List<byte> _filesFinished = new List<byte>();
private static readonly Dictionary<int, FileStream> _streams = new Dictionary<int, FileStream>();
private static readonly List<int> _filesFinished = new List<int>();
public static bool DownloadComplete = false;
public static void AddFile(byte id, string name, long length)
public static void AddFile(int id, string name, long length)
{
string downloadFolder = $"Scripts\\RageCoop\\Resources\\{Main.Settings.LastServerAddress.Replace(":", ".")}";
@ -29,7 +29,7 @@ namespace RageCoop.Client
return;
}
if (!new string[] { ".js", ".xml" }.Any(x => x == Path.GetExtension(name)))
if (!Path.GetExtension(name).EndsWith(".zip"))
{
Cancel(id);
@ -92,7 +92,7 @@ namespace RageCoop.Client
}
}
public static void Write(byte id, byte[] chunk)
public static void Write(int id, byte[] chunk)
{
lock (_filesFinished)
{
@ -132,7 +132,7 @@ namespace RageCoop.Client
}
}
public static void Cancel(byte id)
public static void Cancel(int id)
{
lock (_streams) lock (_downloadFiles) lock (_filesFinished)
{
@ -158,7 +158,7 @@ namespace RageCoop.Client
{
lock (_streams) lock (_downloadFiles) lock (_filesFinished)
{
foreach (KeyValuePair<byte, FileStream> stream in _streams)
foreach (var stream in _streams)
{
stream.Value.Close();
stream.Value.Dispose();
@ -177,7 +177,7 @@ namespace RageCoop.Client
public class DownloadFile
{
public byte FileID { get; set; } = 0;
public int FileID { get; set; } = 0;
public string FileName { get; set; } = string.Empty;
public long FileLength { get; set; } = 0;
public long FileWritten { get; set; } = 0;

View File

@ -53,9 +53,10 @@ namespace RageCoop.Client
{
throw new Exception("Malformed URL");
}
// Send HandshakePacket
EntityPool.AddPlayer();
// Send HandshakePacket
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
new Packets.Handshake()
{
@ -109,13 +110,11 @@ namespace RageCoop.Client
Main.Logger.Debug($"player connected:{p.Username}");
Main.DumpCharacters();
COOPAPI.Connected(packet.PedID);
}
private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
{
var name=PlayerList.GetPlayer(packet.PedID).Username;
GTA.UI.Notification.Show($"{name} left.");
COOPAPI.Disconnected(packet.PedID);
PlayerList.RemovePlayer(packet.PedID);
EntityPool.RemoveAllFromPlayer(packet.PedID);

View File

@ -58,7 +58,6 @@ namespace RageCoop.Client
#endif
COOPAPI.Connected();
Main.QueueAction(() => {
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
@ -88,7 +87,6 @@ namespace RageCoop.Client
CoopMenu.DisconnectedMenuSetting();
#endif
COOPAPI.Disconnected(reason);
Main.QueueAction(() =>
GTA.UI.Notification.Show("~r~Disconnected: " + reason));
@ -191,10 +189,8 @@ namespace RageCoop.Client
Packets.ChatMessage packet = new Packets.ChatMessage();
packet.Unpack(data);
if (!COOPAPI.ChatMessageReceived(packet.Username, packet.Message))
{
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message);return true; });
}
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
}
break;
@ -218,16 +214,9 @@ namespace RageCoop.Client
}
break;
case PacketTypes.Mod:
case PacketTypes.FileTransferChunk:
{
Packets.Mod packet = new Packets.Mod();
packet.Unpack(data);
// Need to do some stuff here
}
break;
case PacketTypes.FileTransferTick:
{
Packets.FileTransferTick packet = new Packets.FileTransferTick();
Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
packet.Unpack(data);
DownloadManager.Write(packet.ID, packet.FileChunk);
@ -239,7 +228,7 @@ namespace RageCoop.Client
Packets.FileTransferRequest packet = new Packets.FileTransferRequest();
packet.Unpack(data);
DownloadManager.AddFile(packet.ID, packet.FileName, packet.FileLength);
DownloadManager.AddFile(packet.ID, packet.Name, packet.FileLength);
}
break;

View File

@ -162,7 +162,7 @@ namespace RageCoop.Client
}
#endif
}
public static void SendDownloadFinish(byte id)
public static void SendDownloadFinish(int id)
{
NetOutgoingMessage outgoingMessage = Client.CreateMessage();

View File

@ -43,6 +43,9 @@
</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="DotNetZip, Version=1.16.0.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.16.0\lib\net40\DotNetZip.dll</HintPath>
</Reference>
<Reference Include="LemonUI.SHVDN3, Version=1.5.1.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libs\Release\scripts\LemonUI.SHVDN3.dll</HintPath>
@ -88,7 +91,7 @@
<Compile Include="Util\VehicleExtensions.cs" />
<Compile Include="Util\WeaponUtil.cs" />
<Compile Include="Networking\Chat.cs" />
<Compile Include="COOPAPI.cs" />
<Compile Include="Scripting\API.cs" />
<Compile Include="Debug.cs" />
<Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Util\TaskType.cs" />
@ -113,6 +116,7 @@
<Compile Include="Util\PedExtensions.cs" />
<Compile Include="WorldThread.cs" />
<Compile Include="Settings.cs" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\RageCoop.Core.csproj">

141
Client/Scripting/API.cs Normal file
View File

@ -0,0 +1,141 @@
#undef DEBUG
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using RageCoop.Core;
namespace RageCoop.Client
{
/// <summary>
/// ?
/// </summary>
public static class API
{
public static class Config
{
/// <summary>
/// Enable automatic respawn.
/// </summary>
public static bool EnableAutoRespawn { get; set; } = true;
/// <summary>
/// Don't show other player's name tag
/// </summary>
public static bool DisplayNameTag { get; set; }=true;
/// <summary>
/// Show other players' blip on map
/// </summary>
public static bool DisplayBlip { get; set; } = true;
}
#region DELEGATES
/// <summary>
/// ?
/// </summary>
/// <param name="connected"></param>
/// <param name="from">The player's id</param>
/// <param name="reason"></param>
public delegate void ConnectEvent(bool connected, int from, string reason = null);
/// <summary>
/// ?
/// </summary>
/// <param name="from"></param>
/// <param name="message">The Lidgren-Network net handle</param>
/// <param name="args"></param>
public delegate void ChatMessage(string from, string message, CancelEventArgs args);
/// <summary>
/// ?
/// </summary>
/// <param name="from">The Lidgren-Network net handle</param>
/// <param name="mod"></param>
/// <param name="customID"></param>
/// <param name="bytes"></param>
#endregion
/// <summary>
/// ?
/// </summary>
public static event ChatMessage OnChatMessage;
/// <summary>
/// Send a local chat message to this player
/// </summary>
/// <param name="from">Username of the player who sent this message</param>
/// <param name="message">The player's message</param>
public static void LocalChatMessage(string from, string message)
{
Main.MainChat.AddMessage(from, message);
}
/// <summary>
/// Disconnect from the server
/// </summary>
public static void Disconnect()
{
Networking.ToggleConnection(null);
}
/// <summary>
/// Check if the player is already on a server
/// </summary>
public static bool IsOnServer
{
get { return Networking.IsOnServer; }
}
/// <summary>
/// Get the local player's ID
/// </summary>
/// <returns>PlayerID</returns>
public static int LocalPlayerID
{
get { return Main.LocalPlayerID; }
}
/// <summary>
/// Check if a RAGECOOP menu is visible
/// </summary>
public static bool IsMenuVisible
{
get { return Menus.CoopMenu.MenuPool.AreAnyVisible; }
}
/// <summary>
/// Check if the RAGECOOP chat is visible
/// </summary>
public static bool IsChatFocused
{
get { return Main.MainChat.Focused; }
}
/// <summary>
/// Check if the RAGECOOP list of players is visible
/// </summary>
public static bool IsPlayerListVisible
{
get { return Util.GetTickCount64() - PlayerList.Pressed < 5000; }
}
/// <summary>
/// Get the version of RAGECOOP
/// </summary>
public static string CurrentVersion
{
get { return Main.CurrentVersion; }
}
/// <summary>
/// Get or set local player's username, set won't be effective if already connected to a server.
/// </summary>
public static string Username
{
get { return Main.Settings.Username; }
set
{
if (IsOnServer || string.IsNullOrEmpty(value))
{
return;
}
Main.Settings.Username = value;
}
}
}
}

View File

@ -54,10 +54,5 @@ namespace RageCoop.Client
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
/// </summary>
public int WorldVehicleSoftLimit { get; set; } = 35;
/// <summary>
/// Disable automatic respawn.
/// </summary>
public bool DisableAutoRespawn { get; set; } = false;
}
}

View File

@ -86,7 +86,7 @@ namespace RageCoop.Client
public override void Update()
{
if (IsPlayer && PedBlip!=null)
if (IsPlayer)
{
if (Username=="N/A")
@ -95,10 +95,18 @@ namespace RageCoop.Client
if (p!=null)
{
Username=p.Username;
PedBlip.Name=Username;
if (PedBlip!=null)
{
PedBlip.Name=Username;
}
}
}
if((!API.Config.DisplayBlip) && (PedBlip!=null))
{
PedBlip.Delete();
PedBlip=null;
}
RenderNameTag();
}
@ -112,10 +120,6 @@ namespace RageCoop.Client
}
bool characterExist = (MainPed != null) && MainPed.Exists();
if (!characterExist)
@ -167,7 +171,6 @@ namespace RageCoop.Client
if (Health <= 0 && !MainPed.IsDead)
{
MainPed.IsInvincible = false;
Main.Logger.Debug($"Killing ped {ID}. Reason:PedDied");
MainPed.Kill();
return;
}
@ -188,7 +191,7 @@ namespace RageCoop.Client
private void RenderNameTag()
{
if (!IsPlayer || !MainPed.IsVisible || !MainPed.IsInRange(Game.Player.Character.Position, 20f))
if (!API.Config.DisplayNameTag || (MainPed==null) || !MainPed.IsVisible || !MainPed.IsInRange(Game.Player.Character.Position, 20f))
{
return;
}
@ -269,13 +272,15 @@ namespace RageCoop.Client
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableShockingEvents, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
SetClothes();
if (IsPlayer)
{
// Add a new blip for the ped
PedBlip=MainPed.AddBlip();
if (API.Config.DisplayBlip)
{
// Add a new blip for the ped
PedBlip=MainPed.AddBlip();
}
MainPed.AttachedBlip.Color = BlipColor.White;
MainPed.AttachedBlip.Scale = 0.8f;
MainPed.AttachedBlip.Name =Username;
@ -295,7 +300,7 @@ namespace RageCoop.Client
}
_lastClothes = Clothes;
}
#region Onfoot
#region ONFOOT
#region -- VARIABLES --
/// <summary>
/// The latest character rotation (may not have been applied yet)
@ -567,14 +572,6 @@ namespace RageCoop.Client
WalkTo();
}
}
private void DisplayInVehicle()
{
if (MainPed.IsOnTurretSeat())
{
Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100);
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
}
}
#region WEAPON
private void CheckCurrentWeapon()
@ -716,5 +713,14 @@ namespace RageCoop.Client
return anim;
}
#endregion
private void DisplayInVehicle()
{
if (MainPed.IsOnTurretSeat())
{
Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100);
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
}
}
}
}

4
Client/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNetZip" version="1.16.0" targetFramework="net48" />
</packages>

View File

@ -74,7 +74,7 @@ namespace RageCoop.Core.Logging
if (LogLevel>4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(),string.Join("\r\n",ex.Message,ex.StackTrace,ex.ToString()), Process.GetCurrentProcess().Id);
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(),"\r\n"+ex.ToString(), Process.GetCurrentProcess().Id);
Buffer+=msg+"\r\n";
}

View File

@ -6,13 +6,18 @@ using Lidgren.Network;
namespace RageCoop.Core
{
public enum FileType:byte
{
Resource=0,
Custom=1,
}
public partial class Packets
{
public class FileTransferRequest : Packet
{
public byte ID { get; set; }
public int ID { get; set; }
public string FileName { get; set; }
public string Name { get; set; }
public long FileLength { get; set; }
@ -24,10 +29,11 @@ namespace RageCoop.Core
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.Add(ID);
byteArray.AddInt(ID);
// The name of the file
byte[] nameBytes = Encoding.UTF8.GetBytes(FileName);
byte[] nameBytes = Encoding.UTF8.GetBytes(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
@ -46,29 +52,29 @@ namespace RageCoop.Core
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadByte();
ID = reader.ReadInt();
int nameArrayLength = reader.ReadInt();
FileName = reader.ReadString(nameArrayLength);
Name = reader.ReadString(nameArrayLength);
FileLength = reader.ReadLong();
#endregion
}
}
public class FileTransferTick : Packet
public class FileTransferChunk : Packet
{
public byte ID { get; set; }
public int ID { get; set; }
public byte[] FileChunk { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FileTransferTick);
message.Write((byte)PacketTypes.FileTransferChunk);
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.Add(ID);
byteArray.AddInt(ID);
// The chunk of the file
byteArray.AddRange(BitConverter.GetBytes(FileChunk.Length));
@ -86,7 +92,7 @@ namespace RageCoop.Core
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadByte();
ID = reader.ReadInt();
int chunkLength = reader.ReadInt();
FileChunk = reader.ReadByteArray(chunkLength);
#endregion
@ -95,7 +101,7 @@ namespace RageCoop.Core
public class FileTransferComplete : Packet
{
public byte ID { get; set; }
public int ID { get; set; }
public override void Pack(NetOutgoingMessage message)
{
@ -105,7 +111,7 @@ namespace RageCoop.Core
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.Add(ID);
byteArray.AddInt(ID);
byte[] result = byteArray.ToArray();
@ -119,7 +125,7 @@ namespace RageCoop.Core
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadByte();
ID = reader.ReadInt();
#endregion
}
}

View File

@ -123,13 +123,14 @@ namespace RageCoop.Core
ChatMessage=10,
NativeCall=11,
NativeResponse=12,
Mod=13,
//Mod=13,
CleanUpWorld=14,
FileTransferTick=15,
FileTransferChunk=15,
FileTransferRequest=16,
FileTransferComplete=17,
ServerClientEvent=18,
ServerClientEvent = 18,
#region Sync
#region INTERVAL
@ -236,58 +237,6 @@ namespace RageCoop.Core
public partial class Packets
{
public class Mod : Packet
{
public string Name { get; set; }
public byte CustomPacketID { get; set; }
public byte[] Bytes { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.Mod);
List<byte> byteArray = new List<byte>();
// Write Name
byte[] nameBytes = Encoding.UTF8.GetBytes(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
// Write CustomPacketID
byteArray.Add(CustomPacketID);
// Write Bytes
byteArray.AddRange(BitConverter.GetBytes(Bytes.Length));
byteArray.AddRange(Bytes);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read Name
int nameLength = reader.ReadInt();
Name = reader.ReadString(nameLength);
// Read CustomPacketID
CustomPacketID = reader.ReadByte();
// Read Bytes
int bytesLength = reader.ReadInt();
Bytes = reader.ReadByteArray(bytesLength);
#endregion
}
}
public class ChatMessage : Packet
{

View File

@ -9,10 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "Client\R
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Resources.Base", "Resources\Base\RageCoop.Resources.Base.csproj", "{9DC11623-8A8B-4D17-B18B-91852922163D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resources", "Resources", "{F49A2617-832B-44DA-9D42-29B5DD09A186}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -45,21 +41,10 @@ Global
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|Any CPU.Build.0 = Release|Any CPU
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.ActiveCfg = Release|Any CPU
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.Build.0 = Release|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|x64.ActiveCfg = Debug|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|x64.Build.0 = Debug|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|Any CPU.Build.0 = Release|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|x64.ActiveCfg = Release|Any CPU
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9DC11623-8A8B-4D17-B18B-91852922163D} = {F49A2617-832B-44DA-9D42-29B5DD09A186}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6CC7EA75-E4FF-4534-8EB6-0AEECF2620B7}
EndGlobalSection

View File

@ -1,17 +1,5 @@
using RageCoop.Server.Scripting;
namespace RageCoop.Resources.Base
{
public class ServerBase :ServerScript
{
public ServerBase()
{
API.RegisterCommand("kick", (ctx) =>
{
if (ctx.Args.Length<1) { return; }
var reason = "EAT POOP!";
if(ctx.Args.Length>=2) { reason=ctx.Args[1]; }
API.GetClientByUsername(ctx.Args[0]).Kick(reason);
});
}
}
}

View File

@ -1 +0,0 @@
using System.Collections.Generic;

View File

@ -14,9 +14,7 @@ namespace RageCoop.Server
private readonly Dictionary<string, object> _customData = new();
private long _callbacksCount = 0;
public readonly Dictionary<long, Action<object>> Callbacks = new();
public bool FilesReceived { get;internal set; } = false;
public bool FilesSent = false;
public bool IsReady { get; internal set; }=false;
#region CUSTOMDATA FUNCTIONS
public void SetData<T>(string name, T data)
{
@ -189,9 +187,9 @@ namespace RageCoop.Server
public void SendTriggerEvent(string eventName, params object[] args)
{
if (!FilesReceived)
if (!IsReady)
{
Program.Logger.Warning($"Player \"{Player.Username}\" doesn't have all the files yet!");
Program.Logger.Warning($"Player \"{Player.Username}\" is not ready!");
return;
}

View File

@ -3,8 +3,8 @@ using System.Linq;
using System.Collections.Generic;
using RageCoop.Core;
using Lidgren.Network;
namespace RageCoop.Server
/*
namespace RageCoop.Server.Obsolete
{
public static class DownloadManager
{
@ -276,3 +276,4 @@ namespace RageCoop.Server
public List<byte[]> FileChunks { get; set; } = null;
}
}
*/

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RageCoop.Core;
namespace RageCoop.Server
{
internal static class EntitiesBlah
{
public static Dictionary<long,SyncedCharacter> Peds=new Dictionary<long,SyncedCharacter>();
public static Dictionary<long, SyncedVehicle> Vehicles = new Dictionary<long, SyncedVehicle>();
}
internal class SyncedVehicle
{
public long Owner { get; set; }
// <index, (enum)VehicleSeat>
// public Dictionary<int, int> Seats=new Dictionary<int, int>();
}
internal class SyncedCharacter
{
public long Owner { get; set; }
}
}

16
Server/FileTransfer.cs Normal file
View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Server
{
internal class FileTransfer
{
public int ID { get; set; }
public float Progress { get; set; }
public string Name { get; set; }
public bool Cancel { get; set; }=false;
}
}

View File

@ -33,11 +33,6 @@ namespace RageCoop.Server
Console.Title = "RAGECOOP";
#endif
if (File.Exists("log.txt"))
{
File.WriteAllText("log.txt", string.Empty);
}
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
{
if (e.SpecialKey == ConsoleSpecialKey.ControlC)
@ -51,8 +46,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Program.Logger.Error(e.InnerException?.Message ?? e.Message);
Console.ReadLine();
Logger.Error($"Fatal error occurred, server shutting down:{e}");
}
}

View File

@ -21,6 +21,10 @@
<None Remove="Obsolete\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DotNetZip" Version="1.16.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\RageCoop.Core.csproj" />
</ItemGroup>

47
Server/Resources.cs Normal file
View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RageCoop.Server.Scripting;
using System.IO;
using Ionic.Zip;
namespace RageCoop.Server
{
internal static class Resources
{
public static readonly Engine ScriptingEngine = new();
/// <summary>
/// Pack client-side resources as a zip file
/// </summary>
public static void PackClientFiles()
{
Program.Logger.Info("Packing client side resources");
}
public static void LoadAll()
{
var path = Path.Combine("Resources", "Client");
Directory.CreateDirectory(path);
var clientResources = Directory.GetDirectories(path);
if (clientResources.Length!=0)
{
// Pack client side resources as a zip file
Program.Logger.Info("Packing client-side resources");
var zippath = Path.Combine(path, "Resources.zip");
if (File.Exists(zippath))
{
File.Delete(zippath);
}
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(path);
zip.Save(zippath);
}
}
ScriptingEngine.LoadAll();
}
}
}

View File

@ -24,8 +24,6 @@ namespace RageCoop.Server.Scripting
public static event EventHandler<HandshakeEventArgs> OnPlayerHandshake;
public static event PlayerConnect OnPlayerConnected;
public static event PlayerDisconnect OnPlayerDisconnected;
// public static event EventHandler OnPlayerUpdate;
#region INVOKE
internal static void InvokeOnStop() { OnStop?.Invoke(); }
internal static void InvokeOnChatMessage(Packets.ChatMessage p,NetConnection con)

View File

@ -24,7 +24,7 @@ namespace RageCoop.Server.Scripting
}
private void LoadResource(string path)
{
foreach(var assembly in Directory.GetFiles(path,"*.dll"))
foreach(var assembly in Directory.GetFiles(path,"*.dll",SearchOption.AllDirectories))
{
LoadScriptsFromAssembly(assembly);
}

View File

@ -12,6 +12,7 @@ using Newtonsoft.Json;
using System.Threading.Tasks;
using Lidgren.Network;
using System.Timers;
using System.Security.Cryptography;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
@ -35,7 +36,8 @@ namespace RageCoop.Server
public static readonly Dictionary<long,Client> Clients = new();
private static System.Timers.Timer SendLatencyTimer = new System.Timers.Timer(5000);
public static readonly Engine ScriptingEngine = new();
private static Dictionary<int,FileTransfer> InProgressFileTransfers=new();
public Server()
{
Program.Logger.Info("================");
@ -169,10 +171,7 @@ namespace RageCoop.Server
}).Start();
#endregion
}
ScriptingEngine.LoadAll();
Program.Logger.Info("Searching for client-side files...");
DownloadManager.CheckForDirectoryAndFiles();
Resources.LoadAll();
Listen();
}
@ -192,25 +191,6 @@ namespace RageCoop.Server
while (!Program.ReadyToStop)
{
// Only new clients that did not receive files on connection will receive the current files in "clientside"
if (DownloadManager.AnyFileExists)
{
lock (Clients)
{
Clients.Values.ToList().ForEach(client =>
{
if (!client.FilesSent)
{
DownloadManager.InsertClient(client.NetID);
client.FilesSent = true;
}
});
}
DownloadManager.Tick();
}
// 15 milliseconds to sleep to reduce CPU usage
Thread.Sleep(15);
@ -269,7 +249,6 @@ namespace RageCoop.Server
{
long nethandle = message.SenderConnection.RemoteUniqueIdentifier;
DownloadManager.RemoveClient(nethandle);
SendPlayerDisconnectPacket(nethandle);
}
@ -280,251 +259,182 @@ namespace RageCoop.Server
break;
}
case NetIncomingMessageType.Data:
// Get packet type
byte btype = message.ReadByte();
var type = (PacketTypes)btype;
switch (type)
{
// Get packet type
byte btype = message.ReadByte();
var type = (PacketTypes)btype;
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
try
{
#region SyncData
case PacketTypes.PedStateSync:
switch (type)
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.PedStateSync packet = new();
packet.Unpack(data);
#region SyncData
PedStateSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.VehicleStateSync:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.VehicleStateSync packet = new();
packet.Unpack(data);
VehicleStateSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.PedSync:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.PedSync packet = new();
packet.Unpack(data);
PedSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.VehicleSync:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.VehicleSync packet = new();
packet.Unpack(data);
VehicleSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.ProjectileSync:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.ProjectileSync packet = new();
packet.Unpack(data);
ProjectileSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
#endregion
case PacketTypes.ChatMessage:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.ChatMessage packet = new();
packet.Unpack(data);
API.Events.InvokeOnChatMessage(packet,message.SenderConnection);
SendChatMessage(packet);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.NativeResponse:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.NativeResponse packet = new();
packet.Unpack(data);
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
if (client != null)
case PacketTypes.PedStateSync:
{
if (client.Callbacks.ContainsKey(packet.ID))
Packets.PedStateSync packet = new();
packet.Unpack(data);
PedStateSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
break;
}
case PacketTypes.VehicleStateSync:
{
Packets.VehicleStateSync packet = new();
packet.Unpack(data);
VehicleStateSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
break;
}
case PacketTypes.PedSync:
{
Packets.PedSync packet = new();
packet.Unpack(data);
PedSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
break;
case PacketTypes.VehicleSync:
{
Packets.VehicleSync packet = new();
packet.Unpack(data);
VehicleSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
break;
case PacketTypes.ProjectileSync:
{
Packets.ProjectileSync packet = new();
packet.Unpack(data);
ProjectileSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
break;
#endregion
case PacketTypes.ChatMessage:
{
try
{
client.Callbacks[packet.ID].Invoke(packet.Args[0]);
client.Callbacks.Remove(packet.ID);
Packets.ChatMessage packet = new();
packet.Unpack(data);
API.Events.InvokeOnChatMessage(packet, message.SenderConnection);
SendChatMessage(packet);
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.FileTransferComplete:
{
try
{
if (DownloadManager.AnyFileExists)
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
break;
Packets.FileTransferComplete packet = new();
case PacketTypes.NativeResponse:
{
Packets.NativeResponse packet = new();
packet.Unpack(data);
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
if (client != null && !client.FilesReceived)
if (client != null)
{
DownloadManager.TryToRemoveClient(client.NetID, packet.ID);
}
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
case PacketTypes.ServerClientEvent:
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.ServerClientEvent packet = new Packets.ServerClientEvent();
packet.Unpack(data);
long senderNetHandle = message.SenderConnection.RemoteUniqueIdentifier;
Client client = null;
lock (Clients)
{
client = Util.GetClientByNetID(senderNetHandle);
}
if (client != null)
{
if (TriggerEvents.Any(x => x.Key.EventName == packet.EventName))
{
EventContext ctx = new()
if (client.Callbacks.ContainsKey(packet.ID))
{
Client = client,
Args = packet.Args.ToArray()
};
TriggerEvents.FirstOrDefault(x => x.Key.EventName == packet.EventName).Value?.Invoke(ctx);
}
else
{
Program.Logger.Warning($"Player \"{client.Player.Username}\" attempted to trigger an unknown event! [{packet.EventName}]");
client.Callbacks[packet.ID].Invoke(packet.Args[0]);
client.Callbacks.Remove(packet.ID);
}
}
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
default:
if (type.IsSyncEvent())
{
// Sync Events
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write(btype);
outgoingMessage.Write(len);
outgoingMessage.Write(data);
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != message.SenderConnection.RemoteUniqueIdentifier).ForEach(x =>
break;
case PacketTypes.ServerClientEvent:
{
Packets.ServerClientEvent packet = new Packets.ServerClientEvent();
packet.Unpack(data);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
long senderNetHandle = message.SenderConnection.RemoteUniqueIdentifier;
Client client = null;
lock (Clients)
{
client = Util.GetClientByNetID(senderNetHandle);
}
});
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
if (client != null)
{
if (TriggerEvents.Any(x => x.Key.EventName == packet.EventName))
{
EventContext ctx = new()
{
Client = client,
Args = packet.Args.ToArray()
};
TriggerEvents.FirstOrDefault(x => x.Key.EventName == packet.EventName).Value?.Invoke(ctx);
}
else
{
Program.Logger.Warning($"Player \"{client.Player.Username}\" attempted to trigger an unknown event! [{packet.EventName}]");
}
}
}
break;
case PacketTypes.FileTransferComplete:
{
Packets.FileTransferComplete packet = new Packets.FileTransferComplete();
packet.Unpack(data);
FileTransfer toRemove;
// Cancel the download if it's in progress
if (InProgressFileTransfers.TryGetValue(packet.ID,out toRemove))
{
toRemove.Cancel=true;
}
}
break;
default:
if (type.IsSyncEvent())
{
// Sync Events
try
{
var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write(btype);
outgoingMessage.Write(len);
outgoingMessage.Write(data);
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
if (toSend.Count!=0)
{
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
else
{
Program.Logger.Error("Unhandled Data / Packet type");
}
break;
}
else
{
Program.Logger.Error("Unhandled Data / Packet type");
}
break;
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
break;
}
break;
case NetIncomingMessageType.ConnectionLatencyUpdated:
{
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
@ -579,7 +489,7 @@ namespace RageCoop.Server
senderConnection.Disconnect(e.Message);
}
#region -- PLAYER --
#region -- SYNC --
// Before we approve the connection, we must shake hands
private void GetHandshake(NetConnection connection, Packets.Handshake packet)
{
@ -649,7 +559,7 @@ namespace RageCoop.Server
return;
}
Program.Logger.Info($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
Program.Logger.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
// Create a new handshake packet
@ -673,18 +583,20 @@ namespace RageCoop.Server
return;
}
List<NetConnection> clients;
if ((clients = Util.FilterAllLocal(local)).Count > 0)
List<NetConnection> clients=MainNetServer.Connections.Exclude(local);
// Send all players to local
if (clients.Count > 0)
{
// Send all players to local
clients.ForEach(targetPlayer =>
clients.ForEach(targetPlayer =>
{
long targetNetHandle = targetPlayer.RemoteUniqueIdentifier;
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
Client targetClient = Util.GetClientByNetID(targetNetHandle);
if (targetClient != null)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
// NetHandle = targetNetHandle,
@ -700,11 +612,13 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
// NetHandle = local.RemoteUniqueIdentifier,
PedID=localClient.Player.PedID,
Username = localClient.Player.Username
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
if(clients.Count > 0)
{
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
}
}
API.Events.InvokePlayerConnected(localClient);
@ -757,10 +671,11 @@ namespace RageCoop.Server
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
foreach (var c in Clients.Values)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
if (c.NetID==client.NetID) { continue; }
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
@ -796,8 +711,7 @@ namespace RageCoop.Server
}
bool isPlayer = packet.ID==client.Player.PedID;
if (isPlayer) { client.Player.Position=packet.Position; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
foreach (var c in Clients.Values)
{
@ -816,6 +730,9 @@ namespace RageCoop.Server
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
@ -990,5 +907,89 @@ namespace RageCoop.Server
RegisterEvent(attribute.EventName, (Action<EventContext>)Delegate.CreateDelegate(typeof(Action<EventContext>), method));
}
}
public static void SendFile(FileStream fs,string name,Client client,Action<float> updateCallback=null)
{
int id = RequestFileID();
FileTransfer transfer = new()
{
ID=id,
Name = name,
};
InProgressFileTransfers.Add(id,transfer);
fs.Seek(0, SeekOrigin.Begin);
Send(
new Packets.FileTransferRequest()
{
FileLength= fs.Length,
Name=name,
ID=id,
},
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered
);
while (fs.CanRead && (!transfer.Cancel))
{
// 4 KB chunk
byte[] chunk = new byte[4096];
int read = fs.Read(chunk, 0, chunk.Length);
if (read!=chunk.Length)
{
// EOF reached
var newchunk = new byte[read];
Array.Copy(chunk, 0, newchunk, 0, read);
chunk=newchunk;
}
Send(
new Packets.FileTransferChunk()
{
ID=id,
FileChunk=chunk,
},
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered
);
transfer.Progress=fs.Position/fs.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress);}
};
Send(
new Packets.FileTransferComplete()
{
ID= id,
}
, client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered
);
InProgressFileTransfers.Remove(id);
}
public static async void SendFileAsync(FileStream fs, string name, Client client,Action<float> updateCallback=null,Action completedCallback=null)
{
SendFile(fs, name, client,updateCallback);
if(completedCallback!=null) { completedCallback(); }
}
public static int RequestFileID()
{
int ID = 0;
while ((ID==0)
|| InProgressFileTransfers.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
RandomNumberGenerator.Create().GetBytes(rngBytes);
// Convert the bytes into an integer
ID = BitConverter.ToInt32(rngBytes, 0);
}
return ID;
}
/// <summary>
/// Pack the packet then send to server.
/// </summary>
/// <param name="p"></param>
/// <param name="channel"></param>
/// <param name="method"></param>
public static void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
p.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, client.Connection,method,(int)channel);
}
}
}

View File

@ -76,9 +76,9 @@ namespace RageCoop.Server
}
// Return a list of all connections but not the local connection
public static List<NetConnection> FilterAllLocal(NetConnection local)
public static List<NetConnection> Exclude(this IEnumerable<NetConnection> connections,NetConnection toExclude)
{
return new(Server.MainNetServer.Connections.Where(e => e != local));
return new(connections.Where(e => e != toExclude));
}
public static List<NetConnection> FilterAllLocal(long local)
{