From 3f235f6e04f1420546f1c76589d731e1dcd733b2 Mon Sep 17 00:00:00 2001 From: Perry Date: Fri, 19 Dec 2025 17:54:50 +0100 Subject: [PATCH] =?UTF-8?q?Funk=C4=8Dn=C3=AD=20komunikace=20mezi=20jedn?= =?UTF-8?q?=C3=ADm=20clientem=20a=20serverem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + .gitmodules | 3 + .idea/.idea.FNAF_Clone/.idea/.gitignore | 13 ++ .idea/.idea.FNAF_Clone/.idea/encodings.xml | 4 + .idea/.idea.FNAF_Clone/.idea/indexLayout.xml | 10 + .idea/.idea.FNAF_Clone/.idea/vcs.xml | 7 + FNAF_Clone.sln | 28 +++ FNAF_Clone.sln.DotSettings.user | 4 + FNAF_Clone/.config/dotnet-tools.json | 36 ++++ FNAF_Clone/.vscode/launch.json | 14 ++ FNAF_Clone/CameraSystem.cs | 18 ++ FNAF_Clone/Client.cs | 103 ++++++++++ FNAF_Clone/ClientPlayer.cs | 8 + FNAF_Clone/CommandManager.cs | 23 +++ FNAF_Clone/Content/Content.mgcb | 15 ++ FNAF_Clone/Content/font.spritefont | 60 ++++++ FNAF_Clone/Content/whitrabt.ttf | Bin 0 -> 13040 bytes FNAF_Clone/FNAF_Clone.csproj | 50 +++++ FNAF_Clone/GameMain.cs | 47 +++++ FNAF_Clone/Icon.bmp | Bin 0 -> 262282 bytes FNAF_Clone/Icon.ico | Bin 0 -> 147541 bytes FNAF_Clone/Input/InputListenerHook.cs | 6 + FNAF_Clone/Input/InputManager.cs | 115 +++++++++++ FNAF_Clone/Input/InputTiming.cs | 7 + FNAF_Clone/Program.cs | 2 + FNAF_Clone/app.manifest | 43 ++++ FNAF_Clone/lib/MonoGameLibrary.dll | 1 + FNAF_Server/FNAF_Server.csproj | 18 ++ FNAF_Server/GameLogic.cs | 12 ++ FNAF_Server/Program.cs | 9 + FNAF_Server/Server.cs | 197 +++++++++++++++++++ FNAF_Server/ServerPlayer.cs | 10 + MonoGameLibrary | 1 + PacketLib/EventQueue.cs | 17 ++ PacketLib/GameEvent.cs | 29 +++ PacketLib/JoinAcceptPacket.cs | 5 + PacketLib/JoinPacket.cs | 5 + PacketLib/NestedTypeManager.cs | 12 ++ PacketLib/NetDataWriterExtensions.cs | 12 ++ PacketLib/PacketLib.csproj | 13 ++ PacketLib/PlayerCommand.cs | 23 +++ PacketLib/PlayerCommandPacket.cs | 5 + PacketLib/PlayerState.cs | 29 +++ PacketLib/UpdatePlayerPacket.cs | 11 ++ 44 files changed, 1030 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .idea/.idea.FNAF_Clone/.idea/.gitignore create mode 100644 .idea/.idea.FNAF_Clone/.idea/encodings.xml create mode 100644 .idea/.idea.FNAF_Clone/.idea/indexLayout.xml create mode 100644 .idea/.idea.FNAF_Clone/.idea/vcs.xml create mode 100644 FNAF_Clone.sln create mode 100644 FNAF_Clone.sln.DotSettings.user create mode 100644 FNAF_Clone/.config/dotnet-tools.json create mode 100644 FNAF_Clone/.vscode/launch.json create mode 100644 FNAF_Clone/CameraSystem.cs create mode 100644 FNAF_Clone/Client.cs create mode 100644 FNAF_Clone/ClientPlayer.cs create mode 100644 FNAF_Clone/CommandManager.cs create mode 100644 FNAF_Clone/Content/Content.mgcb create mode 100755 FNAF_Clone/Content/font.spritefont create mode 100644 FNAF_Clone/Content/whitrabt.ttf create mode 100644 FNAF_Clone/FNAF_Clone.csproj create mode 100644 FNAF_Clone/GameMain.cs create mode 100644 FNAF_Clone/Icon.bmp create mode 100644 FNAF_Clone/Icon.ico create mode 100644 FNAF_Clone/Input/InputListenerHook.cs create mode 100644 FNAF_Clone/Input/InputManager.cs create mode 100644 FNAF_Clone/Input/InputTiming.cs create mode 100644 FNAF_Clone/Program.cs create mode 100644 FNAF_Clone/app.manifest create mode 120000 FNAF_Clone/lib/MonoGameLibrary.dll create mode 100644 FNAF_Server/FNAF_Server.csproj create mode 100644 FNAF_Server/GameLogic.cs create mode 100644 FNAF_Server/Program.cs create mode 100644 FNAF_Server/Server.cs create mode 100644 FNAF_Server/ServerPlayer.cs create mode 160000 MonoGameLibrary create mode 100644 PacketLib/EventQueue.cs create mode 100644 PacketLib/GameEvent.cs create mode 100644 PacketLib/JoinAcceptPacket.cs create mode 100644 PacketLib/JoinPacket.cs create mode 100644 PacketLib/NestedTypeManager.cs create mode 100644 PacketLib/NetDataWriterExtensions.cs create mode 100644 PacketLib/PacketLib.csproj create mode 100644 PacketLib/PlayerCommand.cs create mode 100644 PacketLib/PlayerCommandPacket.cs create mode 100644 PacketLib/PlayerState.cs create mode 100644 PacketLib/UpdatePlayerPacket.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..46ae70e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "MonoGameLibrary"] + path = MonoGameLibrary + url = ./MonoGameLibrary diff --git a/.idea/.idea.FNAF_Clone/.idea/.gitignore b/.idea/.idea.FNAF_Clone/.idea/.gitignore new file mode 100644 index 0000000..e70267d --- /dev/null +++ b/.idea/.idea.FNAF_Clone/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/contentModel.xml +/.idea.FNAF_Clone.iml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.FNAF_Clone/.idea/encodings.xml b/.idea/.idea.FNAF_Clone/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.FNAF_Clone/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.FNAF_Clone/.idea/indexLayout.xml b/.idea/.idea.FNAF_Clone/.idea/indexLayout.xml new file mode 100644 index 0000000..e52aba0 --- /dev/null +++ b/.idea/.idea.FNAF_Clone/.idea/indexLayout.xml @@ -0,0 +1,10 @@ + + + + + + FNAF_Server + + + + \ No newline at end of file diff --git a/.idea/.idea.FNAF_Clone/.idea/vcs.xml b/.idea/.idea.FNAF_Clone/.idea/vcs.xml new file mode 100644 index 0000000..6d0398c --- /dev/null +++ b/.idea/.idea.FNAF_Clone/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/FNAF_Clone.sln b/FNAF_Clone.sln new file mode 100644 index 0000000..3652f00 --- /dev/null +++ b/FNAF_Clone.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNAF_Clone", "FNAF_Clone\FNAF_Clone.csproj", "{069FFFDB-7D61-4AC5-9195-D3664F0B05A0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNAF_Server", "FNAF_Server\FNAF_Server.csproj", "{5E19BD54-CFE0-453A-9CDB-BDB67A4ECB20}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PacketLib", "PacketLib\PacketLib.csproj", "{EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {069FFFDB-7D61-4AC5-9195-D3664F0B05A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {069FFFDB-7D61-4AC5-9195-D3664F0B05A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {069FFFDB-7D61-4AC5-9195-D3664F0B05A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {069FFFDB-7D61-4AC5-9195-D3664F0B05A0}.Release|Any CPU.Build.0 = Release|Any CPU + {5E19BD54-CFE0-453A-9CDB-BDB67A4ECB20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E19BD54-CFE0-453A-9CDB-BDB67A4ECB20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E19BD54-CFE0-453A-9CDB-BDB67A4ECB20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E19BD54-CFE0-453A-9CDB-BDB67A4ECB20}.Release|Any CPU.Build.0 = Release|Any CPU + {EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/FNAF_Clone.sln.DotSettings.user b/FNAF_Clone.sln.DotSettings.user new file mode 100644 index 0000000..eba5de7 --- /dev/null +++ b/FNAF_Clone.sln.DotSettings.user @@ -0,0 +1,4 @@ + + True + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/FNAF_Clone/.config/dotnet-tools.json b/FNAF_Clone/.config/dotnet-tools.json new file mode 100644 index 0000000..fbedee1 --- /dev/null +++ b/FNAF_Clone/.config/dotnet-tools.json @@ -0,0 +1,36 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-mgcb": { + "version": "3.8.4", + "commands": [ + "mgcb" + ] + }, + "dotnet-mgcb-editor": { + "version": "3.8.4", + "commands": [ + "mgcb-editor" + ] + }, + "dotnet-mgcb-editor-linux": { + "version": "3.8.4", + "commands": [ + "mgcb-editor-linux" + ] + }, + "dotnet-mgcb-editor-windows": { + "version": "3.8.4", + "commands": [ + "mgcb-editor-windows" + ] + }, + "dotnet-mgcb-editor-mac": { + "version": "3.8.4", + "commands": [ + "mgcb-editor-mac" + ] + } + } +} \ No newline at end of file diff --git a/FNAF_Clone/.vscode/launch.json b/FNAF_Clone/.vscode/launch.json new file mode 100644 index 0000000..ae71f1a --- /dev/null +++ b/FNAF_Clone/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "C#: FNAF_Clone Debug", + "type": "dotnet", + "request": "launch", + "projectPath": "${workspaceFolder}/FNAF_Clone.csproj" + } + ], + } \ No newline at end of file diff --git a/FNAF_Clone/CameraSystem.cs b/FNAF_Clone/CameraSystem.cs new file mode 100644 index 0000000..c480601 --- /dev/null +++ b/FNAF_Clone/CameraSystem.cs @@ -0,0 +1,18 @@ +namespace FNAF_Clone; + +public class CameraSystem { + public bool Enabled { get; private set; } + public int CurrentCamera { get; private set; } + + public void FlipUp() { + Enabled = true; + } + + public void FlipDown() { + Enabled = false; + } + + public void SetCamera(int camera) { + + } +} \ No newline at end of file diff --git a/FNAF_Clone/Client.cs b/FNAF_Clone/Client.cs new file mode 100644 index 0000000..84e1796 --- /dev/null +++ b/FNAF_Clone/Client.cs @@ -0,0 +1,103 @@ +using System; +using System.Net; +using System.Net.Sockets; +using LiteNetLib; +using LiteNetLib.Utils; +using PacketLib; + +namespace FNAF_Clone; + +public class Client { + private static EventBasedNetListener listener = new(); + private static NetManager client; + private static NetPeer server; + + private static NetDataWriter writer; + private static NetPacketProcessor processor; + public static ClientPlayer Player { get; } = new(); + + + public static void Connect(string endPoint, int port) { + writer = new NetDataWriter(); + processor = new NetPacketProcessor(); + + NestedTypeManager.AutoRegister(processor); + + processor.SubscribeReusable(OnJoinAccept); + processor.SubscribeReusable(OnPlayerUpdate); + + + client = new NetManager(listener){ + AutoRecycle = true + }; + + client.Start(); + Console.WriteLine($"Connecting to server @ {endPoint}:{port}"); + + listener.NetworkReceiveEvent += (peer, reader, channel, method) => { + OnNetworkReceive(peer, reader, method); + }; + + listener.PeerConnectedEvent += peer => { + Console.WriteLine("Connected to Server"); + server = peer; + SendPacket(new JoinPacket {username = "Player1"}, DeliveryMethod.ReliableOrdered); + }; + + client.Connect(endPoint, port, ""); // TODO: figure out how keys work + } + + public static void SendPacket(T packet, DeliveryMethod deliveryMethod) where T : class, new() { + if (server != null) { + writer.Reset(); + processor.Write(writer, packet); + server.Send(writer, deliveryMethod); + } + } + public static void SendCommands(PlayerCommand[] pCommands) { + SendPacket(new PlayerCommandPacket{commands = pCommands}, DeliveryMethod.ReliableOrdered); + } + + public static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { + processor.ReadAllPackets(reader, peer); + } + + public static void OnJoinAccept(JoinAcceptPacket packet) { + Console.WriteLine($"Accepted by server, pid: {packet.state.pid}"); + Player.state = packet.state; + } + + public static void Update() { + client.PollEvents(); + } + + public static void OnPlayerUpdate(UpdatePlayerPacket packet) { + //Player.state = Player.state.pid == 0 ? packet.stateP1 : packet.stateP2; + + foreach (var e in packet.events){ + switch (e.ID){ + case 0: // join + Console.WriteLine("E: Player joined"); + break; + case 1: // leave + Console.WriteLine("E: Player left"); + break; + case 2: // switch cam + if (Player.state.pid == e.Args[0]){ + Player.state.camera = e.Args[1]; + } + Console.WriteLine($"E: player {e.Args[0]} switched to camera {e.Args[1]}"); + break; + case 3: // toggle cam + Player.state.monitorUp = e.Args[1] == 1; + Console.WriteLine($"E: Player {e.Args[0]} toggled monitor {(e.Args[1] == 0 ? "off" : "on")}"); + break; + case -1: // movement + throw new NotImplementedException(); + + } + } + } + + +} \ No newline at end of file diff --git a/FNAF_Clone/ClientPlayer.cs b/FNAF_Clone/ClientPlayer.cs new file mode 100644 index 0000000..4f6efb4 --- /dev/null +++ b/FNAF_Clone/ClientPlayer.cs @@ -0,0 +1,8 @@ +using PacketLib; + +namespace FNAF_Clone; + +public class ClientPlayer { + public PlayerState state; + public string username; +} \ No newline at end of file diff --git a/FNAF_Clone/CommandManager.cs b/FNAF_Clone/CommandManager.cs new file mode 100644 index 0000000..d2321f7 --- /dev/null +++ b/FNAF_Clone/CommandManager.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework.Input; +using MonoGameLibrary.Input; +using PacketLib; + +namespace FNAF_Clone; + +public class CommandManager { + private static (string label, Keys key, Action action)[] keybinds = [ + ("Toggle Camera", Keys.S, ToggleCamera) + ]; + + private static InputListenerHook toggleCamHook = new(true); + + public static void InitInputListeners() { + Array.ForEach(keybinds, tuple => InputManager.AddListener(tuple.label, tuple.key, _ => tuple.action(), InputTiming.PRESS, toggleCamHook)); + } + + private static void ToggleCamera() { + Client.SendCommands([PlayerCommand.TOGGLE_MONITOR()]); + } +} \ No newline at end of file diff --git a/FNAF_Clone/Content/Content.mgcb b/FNAF_Clone/Content/Content.mgcb new file mode 100644 index 0000000..ddc4c36 --- /dev/null +++ b/FNAF_Clone/Content/Content.mgcb @@ -0,0 +1,15 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + diff --git a/FNAF_Clone/Content/font.spritefont b/FNAF_Clone/Content/font.spritefont new file mode 100755 index 0000000..bd33ecf --- /dev/null +++ b/FNAF_Clone/Content/font.spritefont @@ -0,0 +1,60 @@ + + + + + + + Arial + + + 12 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + diff --git a/FNAF_Clone/Content/whitrabt.ttf b/FNAF_Clone/Content/whitrabt.ttf new file mode 100644 index 0000000000000000000000000000000000000000..38e1d7662b5696a554a02871d4bba8ad6091dc5c GIT binary patch literal 13040 zcmd^FU2I&(b)LEVr!^^2B$uS*$ZP)S=Q?8aM^conRl_ubV;HKsUupd?NpB!p|YdNA6i*w&6=6heyvDWE5p_CsnEY}{Jh zRlo0?nS1Y&OT{hnR!X~f?(EE&ne+RdbFZ<+m^;illQO*z|IYSZ&(7}q26|q`=g}ul zOwMH5GJj)?P2=;|M~|O5vVZ>2S!05#F9dD%e--y{A3yoT$b}xYMi!U+ysdX0zF1wwmqcUh|vgkokRc z*qk+Ax^)X+XC}=b^0!;>;qxv&^Z5KbK5wuGd?uGS2z_IU zxDMiD%*XLrWpY@r#iUIaR_=z(vsis2*4TtS1SXHQddw%xHq6;>c49_g%EqJ*K(gJ~ ziy7+QJkmbAd#G<$cUOBM%ey{YJNw#)HxKmnXS=FlX|7@~R_03MrP`HJDVQjQ^_j3# z3RClyxw*>XI=1aXy)`VQdhH5*%(5~*gY$3bV_HW*2hj-hHAX*Cf zWiRTLHyCIKZ|#v;Yol3hHannulxf9kF^uNn21{SW)IiruU&VTNM{}bUJ6GO;Y(u`% zSL3}b-iX^-Qs=&eI(WA9JdXO3oyG**o|n^Kwtq+^+CRlXj!`@xY5$o@DXd)~XM>5b zw74$bKo+{;rCxvrg$;6tXmTQ-0o%80Hv|O1w?{2X2?Q&1hYrnE>aBKx_wlt9rms>% zs+B|c<7+O}E6cjo$+^^g>IIyu1$t;8d{qS;Yg3{fT!VJue|<);nF3dlrQ@$0^kX44 zA6ye^4YcgSvUwIZ;Lp6uw8Mr5U;`uOH_T_uxcOc4dGn+>ZJssf&7YcYn!hmLH$O4g z%umf*=AX?C^RD?1bJM2nYTIq^wxQi=?=z;ae>3-7$iiq=YHV8hr{%Mv!CU;S+cY}; znB{b$o{vKCHgs+?J({(Ahqw6&>Z^>Rky1pLn_U)PZPwd3)Q<^uXwysMjK;g#x&Q6N z`UA~oII4~7H|_L0R?S@gU0vRl>~TNe?gA$zRJ3ZP5*S{FJi@Ym zV>NE8p{xEfhS|aTb&j)xZiq&t`gMPG-5izbtyUABk=_9q3s%0*im4Cv`HhZnqj$kQ zVq*h+euG`uV4+)%leGir-95aSuGjCN%=lQwf_`n+QGZ#_VHe~yxZ$Se-k*!lrZ=e< zcyyj?yc8yO$`~>1c1nJ#PaS}@F}z0iDY+lDZuf_TcPlb(r^ezaFmM-yFm-*fr>+e8NR*OBbg8+g@OoR95feiROS#8P1#-)Jhl}# znEXlWtYZs(XV1s1a?BY888^_srNs*I;@L%#UOdYaE2H9`P#6P*HC}uNE5AcF)<3ja zj*U8Ow*FypjJq(^^Z_6o;7o)8pg=hXU?7(70vr&{$C;7}4ysbqH ziS&U~FA#u{P(iGTLqNTNAw?U3Da9oTHyk=G2u6WEFVSC2Yt+^(6iEF9egOIf+BYDQc#BiaX;T#{7_m@k%&s0S!eoUUBT`t+X0w9P6C{g66DcV&tzGeeS(Qk|u1pFx)aQc@TvoM7v=UffwLthTQU?wfx7nUkp;sYEaTSWg z%f%w>bX>sXP0=x&>u`T@Sfr|)*eJY*BOvn$_`ELzWinW?ejy7c=n0Co^TDv_7Muhb zuArX|!5LcZMG=~3e^}a_SZ}W}_uv@fd0^UP6oDo2tT-qKGr=^fGspRjsepRxGtS_| zh7wRv&+NAeP#PJk45E#SB)NClsRO2>iB2ydP3tu~4mR8e)t(*!GPZ*>9T#Hg37Fxa zBbW%Tk$7!CZoI2k%v}l}OYmTm@*LE7Hk^~FT8^gz^-M`xLT$fY;I3mFPs)xu@iCFM z`VpM+N+O%P6PX^hUHq9_j~>l0iEtW;VZduD&q0RjqF!f9VZ!0nuV21^C4&h)DtVhY zW0zPdRLJ`aF^-(2B0+0v`?&&*ytsISekYZ+hHArA*|jTSXY0*9$d^s$$ndDxYlB9R zW!7gW{aL7R_mHzYY10zROi_3=NmZagDk#+$qzEo=rAFC7pRmNK%Up0gj&|{BnO#P{ zK)7t32f&s@w(tRHGV~5vmp03uG>pWethb7Ph&Ga`s5cOX*g@fQ?TY#a@b9$pS-e8K zF?p-5-_l|k6LNMu=tcq@7He0E#bAQ|J-Al;gdC?4LU$0SZ%A z9)T_Oi4Rf;ClFY?6L2b*kS~WAnRH`1Fj*rOAq`4nuWhGrarM!P37A9ALI@KXHPCr; zJmfx1In^s#(xGvhO4j3aF0Te{FG3&iMV+g^Qh%jLv9g1VhrZZ{>OY3QneweY=>w=W zA>#Km*9{_C{O9RieV`SRplaUjmV^d^HVZsqG5r>)GweBS7Mnm7LoHC;g7}OGebN3# z#p#N#P$UM`K6J?I!`MbL1aU-oP_5j52zWMgr(gA3)zHc9!~1nc-tVK;QB*2q z%MgX%yb+ZeM1&j@HLRZ%l^UX+<&9{Gh@f~T5{w!Vov0Dhp;>^Zmz!OD25+bJSAX^W zU=HU7|D{)7V(`NpOmm|5inmNA);NA*jsT{00vof0So{?sN;fhRV=i9k>U zZ*es6P>zXWy;VU*GT0HsVL3o1DQ8NB6rz*61od@7U2E=*7*PpR+Acv#P`u!n?FowK zcDQzXm81b8{8u@r3^cWe6dTZHlT8Vw_bk#eO*ny~V&yXf%=3NI3Jbi$@YNi_!E96=6 zNIdxPD(kWj7(Nm~nlqdR_wSrYy%nC8knH9hfAbGPk{+oGPu%d7B650r?WmM=Pz397 zC9tSe7J16l3m#WqE=wKj$hj(tU{nNG8B?em9|yyrL`TjYmU0i8r~~X{ldw$y<~b zFRd6LRlRWe@&$^x(LvE|H5w=+BPmo2V#Iq@FI;ZuN$jgrk#3xytMF+H&hgf-L*{sJ zCpO_keIP*r7B3#etGuMxzru%j-yy5a^LufLAAw)$gAY-Ag{kHvlS0yle8c5A5E850 zydwzrv0{wH?%;JHd_%@ACU%Uc#-I(v8c|oM7@vSOEpNdHy^yDKgP=$Qkc;Hl$&;x2 z)$rl_A)~~~tZyPFX;C^K*W~hXfg0Wu3L!&)yLmSIAkqvpqO<Ct7L-SCh|qM@vwT*@502OC~-D(ms)hQvC8K`lo> z8dM*NdMUn*!5W?mcTpKhePda@uw7tEN9@Mrj2WdG+G`^_2Qi3@UeLXwPF3b^TJ;HagJXr_SeJGzW7o1!d3(J*AubYN&d8VLNj?J zG`$~F?}P_#u1>Q+DKLzYB4Ec2jjCFEBOLMk!g=n*`!pgKA?Yi)TGeJ|V4bj%4*;Y* z>|iKT1XOUjkS-zx0F_WMAt;-55L6kY`Z547$>*g!g47vUnvyz>ybgjogDWr)L(1&3 zRut&ycn>k{Cx_&n3OsS-N#zOS13VKeGyY7l|0pJ7#Y9y)Kta>hSP^*VDy(r)(4-qj z{3;e^SSGyERjO1r?89pVv<(rmQ>EavOJoxP51xYRCG!Pg>AN3PthjQjC(^fkq!zOd zZ*zc8^fy}K(i{MSX9v8F&n_WyZrbt2DUc)yssLn;RPwO_)<8H|rGIOWtR&UR$Z3i5 z^F=jzJ4epOhnKV_DRzS7`)*BT5ZAy4JdAKzA-NUp!i*hEwSra^eiCL(o+S&T)h z&CcdJSyU2i(O0hE4Vv;;%_zb|padT(Rzy+3*Yu(q zNoVbAD-!5XTz8MATv7e)kc5=$u@;o@WDd1;atcAtHxl+k-=Lnr1pv1U!UJtr94VNi zK|wr_ytW9iPdmkzyvLS!xa~N`kZKpA=c+grv~W`tWPFWCl&5pALg6Z_g7JODg3vjVU|b1>}m*NOX`bi^#(;-c-oKT#FI%2E}92N)g& z=!P%T0z@I_N+l?hy+RLW@2|f`XXR=T#kwN>qCF1hmF$1b-Y-s#4&j%`QYijFFxK@`cJ&hJB+6$<|ku)Kj8JRe*1;5N2uT6O<(_K`DQlX z&&K;2WmA@CE&eyXrtk~?K3Cuf=1%$#UA$Z1Ed6H}VC&Hxe@))GF|N;#<+YKDpVMr% z|E<|o$Mc&M;}O~u-=n2}L*^m8xA|@JhPjFNGe_-X_UY7))R$BLnhw+F(l4c}nU>6+ z%)^;K$Xv?2k@;nITlU%P)$G6J`g0HDzMcE0+<&jyuxkIR=T=o$y_avz@63NDe>nfm z{M-5YJJz>!v}|iR+VZuQ*IV9g-P$_Q`dsVxTK}c>m#a6gKDhdiR$pEH!J3gZ&#n3H znxCz?*|w+c*|s0H{d{e8?PB|2`&9d-_BYz=9YYDp?*MOd?Hs7^eKMOS?WgcwxVA0)*`K?1VAhzwbnO)0?E0x| zr!nRO*Up%Aw#~J(rrU0D?VK66`#ybg=FIG|qtmA!n)<@QsiRLHpPWSt{~w*2J$3Bl zQ@uO4?bw0aM`kC>Qzs^8pTx`XC(VpGV`j}UbJR@Z_1s?bJ$!cJwE+I>#Y?+q@cmnO zVg5A!O`9q61>AoY*W>01+;79(r_er*>%oZ1DY=_6r!a2TJcI8tMt&CKkAcdR{5lER zpO^cC=s9Yh28~IKUTM^Q=24+`3Uf|^h1}&fJWk%R;*4KC{vcNLyI5)6Z(*fJu&+s+ cValA4JwA!PljaD%SLO^ADf&xAtrZ*p1KdnE%K!iX literal 0 HcmV?d00001 diff --git a/FNAF_Clone/FNAF_Clone.csproj b/FNAF_Clone/FNAF_Clone.csproj new file mode 100644 index 0000000..7c07fc9 --- /dev/null +++ b/FNAF_Clone/FNAF_Clone.csproj @@ -0,0 +1,50 @@ + + + WinExe + net9.0 + Major + false + false + + + app.manifest + Icon.ico + + + + + + + + + Icon.ico + + + Icon.bmp + + + + + + + + + + + + + + lib\MonoGameLibrary.dll + + + + + + + + + + + + + \ No newline at end of file diff --git a/FNAF_Clone/GameMain.cs b/FNAF_Clone/GameMain.cs new file mode 100644 index 0000000..95f2e8f --- /dev/null +++ b/FNAF_Clone/GameMain.cs @@ -0,0 +1,47 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using MonoGameLibrary; +using MonoGameLibrary.Input; + +namespace FNAF_Clone; + +public class GameMain() : Core("fnafkooo", 1920, 1080, false) { + private GraphicsDeviceManager _graphics; + private SpriteBatch _spriteBatch; + + protected override void Initialize() { + Client.Connect("127.0.0.1", 9012); + CommandManager.InitInputListeners(); + + base.Initialize(); + } + + protected override void LoadContent() { + _spriteBatch = new SpriteBatch(GraphicsDevice); + + + // TODO: use this.Content to load your game content here + } + + protected override void Update(GameTime gameTime) { + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || + Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + // TODO: Add your update logic here + + InputManager.NextInputCycle(); + + Client.Update(); + base.Update(gameTime); + } + + protected override void Draw(GameTime gameTime) { + GraphicsDevice.Clear(Color.CornflowerBlue); + + // TODO: Add your drawing code here + + base.Draw(gameTime); + } +} \ No newline at end of file diff --git a/FNAF_Clone/Icon.bmp b/FNAF_Clone/Icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b481653e241818d2894e4ef33234eaff9aad701 GIT binary patch literal 262282 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 RJoIeLznU7AhJ`o~_y6Yy51#-4 literal 0 HcmV?d00001 diff --git a/FNAF_Clone/Input/InputListenerHook.cs b/FNAF_Clone/Input/InputListenerHook.cs new file mode 100644 index 0000000..552e75a --- /dev/null +++ b/FNAF_Clone/Input/InputListenerHook.cs @@ -0,0 +1,6 @@ +namespace FNAF_Clone.Input; + +public class InputListenerHook(bool enabled, bool oneTimeOnly = false) { + public bool Enabled { get; set; } = enabled; + public bool RemoveOnNextTrigger { get; set; } = oneTimeOnly; +} \ No newline at end of file diff --git a/FNAF_Clone/Input/InputManager.cs b/FNAF_Clone/Input/InputManager.cs new file mode 100644 index 0000000..95bd117 --- /dev/null +++ b/FNAF_Clone/Input/InputManager.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework.Input; + +namespace FNAF_Clone.Input; + + +public static class InputManager { + private static Dictionary> keyPressListeners = new(); + private static Dictionary> keyHoldListeners = new(); // The bool is for enabling/disabling that method + private static Dictionary> keyReleaseListeners = new(); + + private static KeyboardState oldState = Keyboard.GetState(); + private static KeyboardState newState; + + // public static ConcurrentQueue inputEventQueue = new(); + + public delegate void KeypressHandler(KeyboardState state); + + + + // public static void StartListening() { + // processInputThread.Start(); + // Console.WriteLine("Started listening for input"); + // } + + public static void NextInputCycle() { + Queue inputEventQueue = new(); + + // Read + + newState = Keyboard.GetState(); + Keys[] pressed = newState.GetPressedKeys(); + Keys[] pressedLastFrame = oldState.GetPressedKeys(); + + Array.ForEach(pressed, key => { + if (oldState.IsKeyUp(key)){ + if (keyPressListeners.TryGetValue(key, out var pressHandlers)){ + pressHandlers.ForEach(t => { + if (t.Item2.Enabled){ + inputEventQueue.Enqueue(t.Item1); + } + if (t.Item2.RemoveOnNextTrigger){ + pressHandlers.Remove(t); + } + }); + } + } + + if (keyHoldListeners.TryGetValue(key, out var holdHandlers)){ + holdHandlers.ForEach(t => { + if (t.Item2.Enabled){ + inputEventQueue.Enqueue(t.Item1); + } + if (t.Item2.RemoveOnNextTrigger){ + holdHandlers.Remove(t); + } + }); + } + }); + + Array.ForEach(pressedLastFrame, key => { + if (!keyReleaseListeners.ContainsKey(key)){ + return; + } + + if (newState.IsKeyUp(key)){ + if (keyReleaseListeners.TryGetValue(key, out var releaseHandlers)) + releaseHandlers.ForEach(t => { + if (t.Item2.Enabled){ + inputEventQueue.Enqueue(t.Item1); + } + if (t.Item2.RemoveOnNextTrigger){ + releaseHandlers.Remove(t); + } + }); + } + }); + oldState = newState; + + // Execute + + foreach (KeypressHandler handler in inputEventQueue){ + handler(newState); + } +} + + // private static Thread processInputThread = new(() => { + // + // }); + + public static void AddListener(Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) { + Dictionary> workingDict; + + switch (timing){ + case InputTiming.PRESS: + workingDict = keyPressListeners; + break; + case InputTiming.HOLD: + workingDict = keyHoldListeners; + break; + case InputTiming.RELEASE: + workingDict = keyReleaseListeners; + break; + default: + throw new ArgumentOutOfRangeException(nameof(timing), timing, null); + } + + if (!workingDict.ContainsKey(key)){ + workingDict.Add(key, new()); + } + + workingDict[key].Add((action, hook)); + } +} \ No newline at end of file diff --git a/FNAF_Clone/Input/InputTiming.cs b/FNAF_Clone/Input/InputTiming.cs new file mode 100644 index 0000000..413a161 --- /dev/null +++ b/FNAF_Clone/Input/InputTiming.cs @@ -0,0 +1,7 @@ +namespace FNAF_Clone.Input; + +public enum InputTiming { + PRESS, + HOLD, + RELEASE +} \ No newline at end of file diff --git a/FNAF_Clone/Program.cs b/FNAF_Clone/Program.cs new file mode 100644 index 0000000..99c5548 --- /dev/null +++ b/FNAF_Clone/Program.cs @@ -0,0 +1,2 @@ +using var game = new FNAF_Clone.GameMain(); +game.Run(); \ No newline at end of file diff --git a/FNAF_Clone/app.manifest b/FNAF_Clone/app.manifest new file mode 100644 index 0000000..2d72dbb --- /dev/null +++ b/FNAF_Clone/app.manifest @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + diff --git a/FNAF_Clone/lib/MonoGameLibrary.dll b/FNAF_Clone/lib/MonoGameLibrary.dll new file mode 120000 index 0000000..1929591 --- /dev/null +++ b/FNAF_Clone/lib/MonoGameLibrary.dll @@ -0,0 +1 @@ +../../MonoGameLibrary/MonoGameLibrary/bin/Debug/net8.0/MonoGameLibrary.dll \ No newline at end of file diff --git a/FNAF_Server/FNAF_Server.csproj b/FNAF_Server/FNAF_Server.csproj new file mode 100644 index 0000000..d13b3e5 --- /dev/null +++ b/FNAF_Server/FNAF_Server.csproj @@ -0,0 +1,18 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + + diff --git a/FNAF_Server/GameLogic.cs b/FNAF_Server/GameLogic.cs new file mode 100644 index 0000000..e8320a8 --- /dev/null +++ b/FNAF_Server/GameLogic.cs @@ -0,0 +1,12 @@ +using PacketLib; + +namespace FNAF_Server; + +public class GameLogic { + + public static void Update() { + + } + + +} \ No newline at end of file diff --git a/FNAF_Server/Program.cs b/FNAF_Server/Program.cs new file mode 100644 index 0000000..1a0d1d6 --- /dev/null +++ b/FNAF_Server/Program.cs @@ -0,0 +1,9 @@ +using System.Security.Cryptography.X509Certificates; + +namespace FNAF_Server; + +public class Program { + public static void Main(string[] args) { + Server.Start(9012); + } +} \ No newline at end of file diff --git a/FNAF_Server/Server.cs b/FNAF_Server/Server.cs new file mode 100644 index 0000000..acccff3 --- /dev/null +++ b/FNAF_Server/Server.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.Threading; +using LiteNetLib; +using LiteNetLib.Utils; +using PacketLib; + +namespace FNAF_Server; + +public class Server { + public static ServerPlayer P1; + public static ServerPlayer P2; + + private static EventBasedNetListener listener; + private static NetManager server; + + private const int TargetTickRate = 30; + private const double TargetDeltaTime = 1.0 / TargetTickRate; + private const long TargetTickTimeMs = 1000 / TargetTickRate; + + private static NetDataWriter writer; + private static NetPacketProcessor processor; + private static Dictionary players = new(); + + private static bool isRunning; + + public static void Start(int port) { + writer = new NetDataWriter(); + processor = new NetPacketProcessor(); + + NestedTypeManager.AutoRegister(processor); + + processor.SubscribeReusable(OnJoinReceived); + processor.SubscribeReusable(OnCommandReceived); + + listener = new EventBasedNetListener(); // ← Initialize the listener! + + + server = new NetManager(listener){ + AutoRecycle = true + }; + + Console.WriteLine($"Starting server on {port}"); + + listener.ConnectionRequestEvent += request => { + Console.WriteLine($"Connection Request from {request.RemoteEndPoint}"); + if (players.Count >= 2){ + Console.WriteLine($"{request.RemoteEndPoint} denied, server full"); + return; + } + request.Accept(); + }; + + listener.NetworkReceiveEvent += (peer, reader, channel, method) => { + OnNetworkReceive(peer, reader, method); + }; + + listener.PeerDisconnectedEvent += (peer, info) => { + OnPeerDisconnected(peer, info); + }; + + server.Start(port); + + Run(); + } + + public static void SendPacket(T packet, NetPeer peer, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() { + if (peer != null) { + writer.Reset(); + processor.Write(writer, packet); + peer.Send(writer, deliveryMethod); + } + } + public static void SendPacket(T packet, uint pid, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() { + SendPacket(packet, players[pid].peer, deliveryMethod); + } + public static void SendPacketToAll(T packet, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() { + foreach (var player in players.Values){ + SendPacket(packet, player.peer, deliveryMethod); + } + } + + public static void SendUpdate(GameEvent[] gevents, uint pid) { + // SendPacket(new UpdatePlayerPacket{eventQueue = new EventQueue{Events = events}}, pid); + SendPacket(new UpdatePlayerPacket{events = gevents}, pid); + + } + + public static void SendUpdateToAll(GameEvent[] gevents) { + // SendPacketToAll(new UpdatePlayerPacket{eventQueue = new EventQueue{Events = events}}); + SendPacketToAll(new UpdatePlayerPacket{events = gevents}); + + } + + + public static void OnJoinReceived(JoinPacket packet, NetPeer peer) { + Console.WriteLine($"Received join from {packet.username} (pid: {(uint)peer.Id})"); + + ServerPlayer newPlayer = (players[(uint)peer.Id] = new ServerPlayer { + peer = peer, + state = new PlayerState { + pid = (uint)peer.Id, + camera = 0 + }, + username = packet.username + }); + + SendPacket(new JoinAcceptPacket { state = newPlayer.state }, peer); + + if (players.Count == 1){ + P1 = newPlayer; + } + else{ + P2 = newPlayer; + } + } + + public static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { + processor.ReadAllPackets(reader, peer); + } + + public static void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { + if (peer.Tag != null) { + players.Remove((uint)peer.Id); + } + } + + public static void OnCommandReceived(PlayerCommandPacket packet, NetPeer peer) { + PlayerCommand[] commands = packet.commands; + + foreach (var playerCommand in commands){ + switch (playerCommand.ID){ + case 0: + Console.WriteLine($"C: Player {peer.Id} switched to camera {playerCommand.Args[0]}"); + SendUpdateToAll([GameEvent.SWITCH_CAM(peer.Id, playerCommand.Args[0])]); + break; + case 1: + bool newState = !players[(uint)peer.Id].state.monitorUp; + players[(uint)peer.Id].state.monitorUp = newState; + Console.WriteLine($"C: Player {peer.Id} toggled camera {(newState ? "on" : "off")}"); + SendUpdateToAll([GameEvent.TOGGLE_MONITOR(peer.Id, newState)]); + break; + } + } + } + + public static void Update() { + server.PollEvents(); + // Console.WriteLine("update"); + GameLogic.Update(); // TODO: add a parameter for player input + } + + public static void Run(CancellationToken cancellationToken = default) + { + isRunning = true; + var stopwatch = Stopwatch.StartNew(); + long previousTicks = 0; + + while (isRunning && !cancellationToken.IsCancellationRequested) + { + long currentTicks = stopwatch.ElapsedMilliseconds; + long elapsed = currentTicks - previousTicks; + + if (elapsed >= TargetTickTimeMs) + { + Update(); + + previousTicks = currentTicks; + if (elapsed > TargetTickTimeMs * 2) + { + Console.WriteLine($"Warning: Tick took {elapsed}ms (target: {TargetTickTimeMs}ms)"); + } + } + else + { + + int sleepTime = (int)(TargetTickTimeMs - elapsed - 2); + if (sleepTime > 0) + { + Thread.Sleep(sleepTime); + } + + while (stopwatch.ElapsedMilliseconds - previousTicks < TargetTickTimeMs){ + Thread.SpinWait(1); + } + } + } + } + + public static void Stop() + { + isRunning = false; + } +} + + diff --git a/FNAF_Server/ServerPlayer.cs b/FNAF_Server/ServerPlayer.cs new file mode 100644 index 0000000..2afafdd --- /dev/null +++ b/FNAF_Server/ServerPlayer.cs @@ -0,0 +1,10 @@ +using LiteNetLib; +using PacketLib; + +namespace FNAF_Server; + +public class ServerPlayer { + public NetPeer peer; + public PlayerState state; + public string username; +} diff --git a/MonoGameLibrary b/MonoGameLibrary new file mode 160000 index 0000000..a2b524e --- /dev/null +++ b/MonoGameLibrary @@ -0,0 +1 @@ +Subproject commit a2b524ee04772d483750047c997172cc62090a9a diff --git a/PacketLib/EventQueue.cs b/PacketLib/EventQueue.cs new file mode 100644 index 0000000..dea8a72 --- /dev/null +++ b/PacketLib/EventQueue.cs @@ -0,0 +1,17 @@ +using LiteNetLib.Utils; + +namespace PacketLib; + +public struct EventQueue : INetSerializable { + public GameEvent[] Events{ get; set; } + + public void Serialize(NetDataWriter writer) { + writer.PutArray(Events); + } + + public void Deserialize(NetDataReader reader) { + Events = reader.GetArray(); + } + + // public GameEvent this[int index] => Events[index]; +} \ No newline at end of file diff --git a/PacketLib/GameEvent.cs b/PacketLib/GameEvent.cs new file mode 100644 index 0000000..615894d --- /dev/null +++ b/PacketLib/GameEvent.cs @@ -0,0 +1,29 @@ +using System.Diagnostics.CodeAnalysis; +using LiteNetLib.Utils; + +namespace PacketLib; + +#nullable disable +public struct GameEvent : INetSerializable{ + public static GameEvent PLAYER_JOIN(int pid) => new(){ID = 0, Args = [pid] }; + public static GameEvent PLAYER_LEAVE(int pid) => new(){ID = 1, Args = [pid] }; + public static GameEvent SWITCH_CAM(int pid, int camId) => new(){ID = 2, Args = [pid, camId] }; + public static GameEvent TOGGLE_MONITOR(int pid, bool state) => new(){ID = 3, Args = [pid, state ? 1 : 0]}; + + public static GameEvent ENEMY_MOVEMENT(int enemyId, int camId) => new(){ID = -1, Args = [enemyId, camId]}; + + public int ID{ get; set; } + public bool Hideable => ID < 0; + public int[] Args{ get; private set; } + + public void Serialize(NetDataWriter writer) { + writer.Put(ID); + writer.PutArray(Args); + } + + public void Deserialize(NetDataReader reader) { + ID = reader.GetInt(); + Args = reader.GetIntArray(); + } + +} \ No newline at end of file diff --git a/PacketLib/JoinAcceptPacket.cs b/PacketLib/JoinAcceptPacket.cs new file mode 100644 index 0000000..178249a --- /dev/null +++ b/PacketLib/JoinAcceptPacket.cs @@ -0,0 +1,5 @@ +namespace PacketLib; + +public class JoinAcceptPacket { + public PlayerState state { get; set; } +} \ No newline at end of file diff --git a/PacketLib/JoinPacket.cs b/PacketLib/JoinPacket.cs new file mode 100644 index 0000000..c009883 --- /dev/null +++ b/PacketLib/JoinPacket.cs @@ -0,0 +1,5 @@ +namespace PacketLib; + +public class JoinPacket { + public string username { get; set; } +} \ No newline at end of file diff --git a/PacketLib/NestedTypeManager.cs b/PacketLib/NestedTypeManager.cs new file mode 100644 index 0000000..c479d8c --- /dev/null +++ b/PacketLib/NestedTypeManager.cs @@ -0,0 +1,12 @@ +using LiteNetLib.Utils; + +namespace PacketLib; + +public static class NestedTypeManager { + public static void AutoRegister(NetPacketProcessor processor) { + processor.RegisterNestedType(); + processor.RegisterNestedType(); + processor.RegisterNestedType(); + processor.RegisterNestedType(); + } +} \ No newline at end of file diff --git a/PacketLib/NetDataWriterExtensions.cs b/PacketLib/NetDataWriterExtensions.cs new file mode 100644 index 0000000..4f74393 --- /dev/null +++ b/PacketLib/NetDataWriterExtensions.cs @@ -0,0 +1,12 @@ +using System.Xml; +using LiteNetLib.Utils; + +namespace PacketLib; + +public static class NetDataWriterExtensions { + public static GameEvent GetGameEvent(this NetDataReader reader) { + GameEvent gevent = new(); + gevent.Deserialize(reader); + return gevent; + } +} \ No newline at end of file diff --git a/PacketLib/PacketLib.csproj b/PacketLib/PacketLib.csproj new file mode 100644 index 0000000..06e6adc --- /dev/null +++ b/PacketLib/PacketLib.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + diff --git a/PacketLib/PlayerCommand.cs b/PacketLib/PlayerCommand.cs new file mode 100644 index 0000000..a800f4f --- /dev/null +++ b/PacketLib/PlayerCommand.cs @@ -0,0 +1,23 @@ +using LiteNetLib.Utils; + +namespace PacketLib; + +public struct PlayerCommand : INetSerializable { + public static PlayerCommand SWITCH_CAM(int camId) => new(){ID = 0, Args = [camId] }; + public static PlayerCommand TOGGLE_MONITOR() => new(){ID = 1, Args = []}; + + public int ID{ get; set; } + public bool Hideable => ID < 0; + public int[] Args{ get; private set; } + + public void Serialize(NetDataWriter writer) { + writer.Put(ID); + writer.PutArray(Args); + } + + public void Deserialize(NetDataReader reader) { + ID = reader.GetInt(); + Args = reader.GetIntArray(); + } + +} \ No newline at end of file diff --git a/PacketLib/PlayerCommandPacket.cs b/PacketLib/PlayerCommandPacket.cs new file mode 100644 index 0000000..8e13f3d --- /dev/null +++ b/PacketLib/PlayerCommandPacket.cs @@ -0,0 +1,5 @@ +namespace PacketLib; + +public class PlayerCommandPacket { + public PlayerCommand[] commands { get; set; } +} \ No newline at end of file diff --git a/PacketLib/PlayerState.cs b/PacketLib/PlayerState.cs new file mode 100644 index 0000000..f8485d8 --- /dev/null +++ b/PacketLib/PlayerState.cs @@ -0,0 +1,29 @@ +using LiteNetLib; +using LiteNetLib.Utils; + +namespace PacketLib; + +public struct PlayerState : INetSerializable { + public uint pid; + public int camera; + public bool monitorUp; + + public void Serialize(NetDataWriter writer) { + writer.Put(pid); + writer.Put(camera); + writer.Put(monitorUp); + } + + public void Deserialize(NetDataReader reader) { + pid = reader.GetUInt(); + camera = reader.GetInt(); + monitorUp = reader.GetBool(); + } +} + + + + + + + diff --git a/PacketLib/UpdatePlayerPacket.cs b/PacketLib/UpdatePlayerPacket.cs new file mode 100644 index 0000000..1e0d663 --- /dev/null +++ b/PacketLib/UpdatePlayerPacket.cs @@ -0,0 +1,11 @@ +namespace PacketLib; + +public class UpdatePlayerPacket { + // public PlayerState stateP1{ get; set; } + // public PlayerState stateP2{ get; set; } + + // TODO: implement anti-desync measures by comparing server and client states + + public GameEvent[] events { get; set; } + // public EventQueue eventQueue { get; set; } +} \ No newline at end of file