Oprava spawnování monster, optimalizace v CommandProcessor a EventProcessor. Přesunutí některých tříd do vlastních namespaců, pročištění kódu, úpravy formátování, odstranění nepoužívaných souborů a zakomentovaného kódu
This commit is contained in:
parent
e5d746d597
commit
243f071a43
62 changed files with 873 additions and 1217 deletions
135
ONDClient/Net/Client.cs
Normal file
135
ONDClient/Net/Client.cs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
using ONDClient.Map;
|
||||
using PacketLib;
|
||||
|
||||
namespace ONDClient.Net;
|
||||
|
||||
public static class Client {
|
||||
public enum ConnectionState {
|
||||
IDLE,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
ACCEPTED,
|
||||
GAME_STARTING,
|
||||
DISCONNECTED,
|
||||
GAME_IN_PROGRESS,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public static string StatusText{ get; private set; }
|
||||
|
||||
public static ConnectionState State { get; set; } = ConnectionState.IDLE;
|
||||
|
||||
public static ClientPlayer Player { get; } = new();
|
||||
public static ClientPlayer Opponent { get; } = new();
|
||||
|
||||
private static EventBasedNetListener listener = new();
|
||||
private static NetManager client;
|
||||
private static NetPeer server;
|
||||
|
||||
private static NetDataWriter writer;
|
||||
private static NetPacketProcessor processor;
|
||||
|
||||
public static ClientPlayer GetPlayer(int pid) => Player.State.Pid == pid ? Player : Opponent;
|
||||
|
||||
public static void Init() {
|
||||
writer = new NetDataWriter();
|
||||
processor = new NetPacketProcessor();
|
||||
|
||||
NestedTypeManager.AutoRegister(processor);
|
||||
|
||||
processor.SubscribeReusable<JoinAcceptPacket>(OnJoinAccept);
|
||||
processor.SubscribeReusable<UpdatePlayerPacket>(OnPlayerUpdate);
|
||||
processor.SubscribeReusable<MapInitPacket>(OnMapInit);
|
||||
processor.SubscribeReusable<OpponentInitPacket>(OnOpponentInit);
|
||||
|
||||
client = new NetManager(listener){
|
||||
AutoRecycle = true
|
||||
};
|
||||
|
||||
listener.NetworkReceiveEvent += (peer, reader, channel, method) => {
|
||||
OnNetworkReceive(peer, reader, method);
|
||||
};
|
||||
|
||||
listener.PeerConnectedEvent += peer => {
|
||||
Console.WriteLine("Connected to Server");
|
||||
server = peer;
|
||||
State = ConnectionState.CONNECTED;
|
||||
SendPacket(new JoinPacket {Username = Player.Username == "" ? "Anonymous" : Player.Username}, DeliveryMethod.ReliableOrdered);
|
||||
};
|
||||
|
||||
listener.PeerDisconnectedEvent += (peer, info) => {
|
||||
State = ConnectionState.IDLE;
|
||||
Console.WriteLine("Disconnected from Server:\n" + info.Reason);
|
||||
State = ConnectionState.DISCONNECTED;
|
||||
StatusText = info.Reason.ToString();
|
||||
};
|
||||
}
|
||||
|
||||
public static void Connect(string endPoint, int port) {
|
||||
State = ConnectionState.CONNECTING;
|
||||
|
||||
client.Start();
|
||||
Console.WriteLine($"Connecting to server @ {endPoint}:{port}");
|
||||
|
||||
new Thread(() => client.Connect(endPoint, port, "")).Start();
|
||||
}
|
||||
|
||||
public static void SendPacket<T>(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);
|
||||
}
|
||||
|
||||
private static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) {
|
||||
processor.ReadAllPackets(reader, peer);
|
||||
}
|
||||
|
||||
private static void OnJoinAccept(JoinAcceptPacket packet) {
|
||||
Console.WriteLine($"Accepted by server, pid: {packet.State.Pid}");
|
||||
State = ConnectionState.ACCEPTED;
|
||||
Player.State = packet.State;
|
||||
}
|
||||
|
||||
public static void Update() {
|
||||
client.PollEvents();
|
||||
}
|
||||
|
||||
public static void OnPlayerUpdate(UpdatePlayerPacket packet) {
|
||||
EventProcessor.Evaluate(packet.Events);
|
||||
}
|
||||
|
||||
public static void Disconnect() {
|
||||
if (client == null || client.ConnectedPeersCount == 0) return;
|
||||
client.DisconnectAll();
|
||||
client.Stop();
|
||||
State = ConnectionState.IDLE;
|
||||
}
|
||||
|
||||
public static void StartServer() {
|
||||
Process.Start("ONDServer");
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
private static void OnMapInit(MapInitPacket packet) {
|
||||
ClientMapManager.InitMap(packet.Connectors, packet.YourTiles, packet.OpponentTiles, packet.LitTiles, packet.UpsideDown);
|
||||
}
|
||||
|
||||
private static void OnOpponentInit(OpponentInitPacket packet) {
|
||||
Opponent.State = packet.State;
|
||||
Opponent.Username = packet.Username;
|
||||
State = ConnectionState.GAME_STARTING;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
8
ONDClient/Net/ClientPlayer.cs
Normal file
8
ONDClient/Net/ClientPlayer.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using PacketLib;
|
||||
|
||||
namespace ONDClient.Net;
|
||||
|
||||
public class ClientPlayer {
|
||||
public PlayerState State;
|
||||
public string Username;
|
||||
}
|
||||
97
ONDClient/Net/CommandManager.cs
Normal file
97
ONDClient/Net/CommandManager.cs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GlobalClassLib;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGameLibrary.Input;
|
||||
using ONDClient.GUI;
|
||||
using ONDClient.Map;
|
||||
using ONDClient.Sound;
|
||||
using PacketLib;
|
||||
|
||||
namespace ONDClient.Net;
|
||||
|
||||
public static class CommandManager {
|
||||
private static readonly (string label, Keys key, Action action)[] keybinds = [
|
||||
("Toggle camera", Keys.Space, SendToggleCamera),
|
||||
("Toggle left door", Keys.A, ToggleDoorLeft),
|
||||
("Toggle centre door", Keys.W, ToggleDoorCentre),
|
||||
("Toggle right door", Keys.D, ToggleDoorRight),
|
||||
("Toggle back door", Keys.S, ToggleDoorBack),
|
||||
("Toggle light", Keys.F, ToggleLight)
|
||||
];
|
||||
|
||||
private static InputListenerHook AllControlsHook{ get; } = new(true);
|
||||
|
||||
public static void InitInputListeners() {
|
||||
Array.ForEach(keybinds, tuple => InputManager.AddListener(tuple.label, tuple.key, () => tuple.action(), InputTiming.PRESS, AllControlsHook));
|
||||
}
|
||||
|
||||
public static void AllowGameControls(bool state) {
|
||||
AllControlsHook.Enabled = state;
|
||||
}
|
||||
|
||||
private static void SendToggleCamera() {
|
||||
Client.Player.State.MonitorUp = !Client.Player.State.MonitorUp;
|
||||
UIManager.ChangeMonitorState(Client.Player.State.MonitorUp);
|
||||
Client.SendCommands([PlayerCommand.SET_MONITOR(Client.Player.State.MonitorUp)]);
|
||||
SoundManager.PlayMonitorFlip();
|
||||
}
|
||||
|
||||
private static void ToggleDoorLeft() => SendToggleDoor(Direction.EAST);
|
||||
private static void ToggleDoorCentre() => SendToggleDoor(Direction.NORTH);
|
||||
private static void ToggleDoorRight() => SendToggleDoor(Direction.WEST);
|
||||
private static void ToggleDoorBack() => SendToggleDoor(Direction.SOUTH);
|
||||
|
||||
private static Dictionary<Direction, TileConnectorProjection> currentDoorBinds = new();
|
||||
|
||||
private static void ToggleLight() => SendToggleLight(Client.Player.State.Camera, !ClientMapManager.Get(Client.Player.State.Camera).Lit);
|
||||
|
||||
private static void SendToggleDoor(Direction direction) {
|
||||
if (Screen.CurrentScreen.Label == UIManager.ScreenTypes.CAMERAS){
|
||||
SendToggleRemoteDoor(direction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction == Direction.SOUTH) return;
|
||||
Client.Player.State.DoorStates[(int)direction] = !Client.Player.State.DoorStates[(int)direction];
|
||||
UIManager.ChangeDoorState(direction, Client.Player.State.DoorStates[(int)direction]);
|
||||
SoundManager.PlayDoor(Client.Player.State.DoorStates[(int)direction]);
|
||||
Client.SendCommands([PlayerCommand.SET_DOOR_OFFICE(direction, Client.Player.State.DoorStates[(int)direction])]);
|
||||
}
|
||||
|
||||
private static void SendToggleRemoteDoor(Direction direction) {
|
||||
if (!currentDoorBinds.TryGetValue(direction, out var connector)) return;
|
||||
if (connector.Owner != Client.Player) return;
|
||||
|
||||
connector.Blocked = !connector.Blocked;
|
||||
Client.SendCommands([PlayerCommand.SET_DOOR_REMOTE(connector.Id, connector.Blocked)]);
|
||||
SoundManager.PlayDoorRemote(connector.Blocked);
|
||||
UIManager.ChangeRemoteDoorState(connector.Id, connector.Blocked);
|
||||
}
|
||||
|
||||
public static void SendChangeCamera(int id) {
|
||||
if (id == Client.Player.State.OfficeTileId || id == Client.Opponent.State.OfficeTileId) return;
|
||||
|
||||
Client.Player.State.Camera = id;
|
||||
UIManager.ChangeCamera(id);
|
||||
SoundManager.PlayCameraSwitch();
|
||||
Client.SendCommands([PlayerCommand.SWITCH_CAM(id)]);
|
||||
MapTileProjection tile = ClientMapManager.Get(id);
|
||||
|
||||
currentDoorBinds.Clear();
|
||||
foreach (var c in tile.GetAllConnectors().Where(c => c.Type == ConnectorType.DOOR_REMOTE)){
|
||||
Direction dir = c.GetDirection(tile);
|
||||
if (dir == Direction.NONE) return;
|
||||
currentDoorBinds.Add(dir, c);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendToggleLight(int id, bool state) {
|
||||
if(!Client.Player.State.MonitorUp || ClientMapManager.Get(id).Owner != Client.Player) return;
|
||||
ClientMapManager.Get(id).Lit = state;
|
||||
UIManager.UpdateCameras([id]);
|
||||
SoundManager.PlayLight(state);
|
||||
Client.SendCommands([PlayerCommand.SET_LIGHT(id, state)]);
|
||||
}
|
||||
}
|
||||
212
ONDClient/Net/EventProcessor.cs
Normal file
212
ONDClient/Net/EventProcessor.cs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GlobalClassLib;
|
||||
using ONDClient.Enemies;
|
||||
using ONDClient.GUI;
|
||||
using ONDClient.Map;
|
||||
using ONDClient.Sound;
|
||||
using PacketLib;
|
||||
|
||||
namespace ONDClient.Net;
|
||||
|
||||
public static class EventProcessor {
|
||||
|
||||
private static readonly Dictionary<int, Action<GameEvent>> eventHandlers = new(){
|
||||
[0] = _ => PlayerJoin(),
|
||||
[1] = _ => PlayerLeave(),
|
||||
[2] = e => SwitchCam(e.Args[0], e.Args[1]),
|
||||
[3] = e => ChangeMonitorState(e.Args[0], e.Args[1] == 1),
|
||||
[4] = e => ChangeDoorStateOffice(e.Args[0], e.Args[1], e.Args[2] == 1),
|
||||
[5] = e => ChangeDoorStateRemote(e.Args[0], (e.Args[1], e.Args[2]), e.Args[3] == 1),
|
||||
[6] = e => SpawnEnemy((EnemyType)e.Args[0], e.Args[1], e.Args[3]),
|
||||
[7] = e => MoveEnemy(e.Args[0], e.Args[1]),
|
||||
[8] = e => EnemyAttack(e.Args[0], e.Args[1]),
|
||||
[9] = e => EnemyReset(e.Args[0], e.Args[1]),
|
||||
[10] = e => SpotSetActive(e.Args[0], e.Args[1] == 1),
|
||||
[11] = e => PlayerWin(e.Args[0]),
|
||||
[12] = _ => StartGame(),
|
||||
[13] = e => PowerTick(e.Args[0], e.Args[1]),
|
||||
[14] = e => PowerOut(e.Args[0]),
|
||||
[15] = e => ChangeLightState(e.Args[0], e.Args[1], e.Args[2] == 1),
|
||||
[16] = _ => NekoAnger(),
|
||||
[17] = _ => VentWalk(),
|
||||
[18] = _ => NextPhase()
|
||||
};
|
||||
|
||||
public static void Evaluate(GameEvent[] events) {
|
||||
foreach (var e in events){
|
||||
eventHandlers[e.Id](e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PlayerJoin() {
|
||||
Console.WriteLine("E: Player joined");
|
||||
}
|
||||
private static void PlayerLeave() {
|
||||
Console.WriteLine("E: Player left");
|
||||
}
|
||||
private static void SwitchCam(int pid, int camId) {
|
||||
if (Client.Player.State.Pid != pid){
|
||||
UIManager.ChangeCameraOpponent(camId);
|
||||
return;
|
||||
}
|
||||
if (Client.Player.State.Camera != camId) Console.WriteLine("!!! DESYNC: CAMERA STATE");
|
||||
Console.WriteLine($"E: player {pid} switched to camera {camId}");
|
||||
}
|
||||
private static void ChangeMonitorState(int pid, bool state) {
|
||||
Console.WriteLine($"E: Player {pid} toggled monitor {(state ? "off" : "on")}");
|
||||
|
||||
if (pid != Client.Player.State.Pid){
|
||||
UIManager.ChangeMonitorStateOpponent(state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Client.Player.State.MonitorUp != state) Console.WriteLine("!!! DESYNC: MONITOR STATE");
|
||||
}
|
||||
private static void ChangeDoorStateOffice(int pid, int doorId, bool state) {
|
||||
Console.WriteLine($"E: Player {pid} {(state ? "closed" : "opened")} door {doorId}");
|
||||
|
||||
if (pid == Client.Player.State.Pid){
|
||||
if (Client.Player.State.DoorStates[doorId] != state) Console.WriteLine("!!! DESYNC: DOOR STATE");
|
||||
return;
|
||||
}
|
||||
|
||||
Client.Opponent.State.DoorStates[doorId] = state;
|
||||
UIManager.ChangeDoorStateOpponent((Direction)doorId, state);
|
||||
}
|
||||
|
||||
private static void ChangeDoorStateRemote(int pid, (int tile1, int tile2) connectorId, bool state) {
|
||||
Console.WriteLine($"E: Player {pid} {(state ? "closed" : "opened")} door {connectorId.tile1}-{connectorId.tile2}");
|
||||
TileConnectorProjection connector = ClientMapManager.GetConnector(connectorId);
|
||||
if (connector == null){
|
||||
Console.WriteLine($"!!! Connector {connectorId} not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == Client.Player.State.Pid){
|
||||
if (connector.Blocked != state) Console.WriteLine("!!! DESYNC: REMOTE DOOR STATE");
|
||||
return;
|
||||
}
|
||||
|
||||
connector.Blocked = state;
|
||||
UIManager.ChangeRemoteDoorState(connectorId, state);
|
||||
}
|
||||
|
||||
private static void SpawnEnemy(EnemyType type, int id, int camId) {
|
||||
Console.WriteLine($"E: Spawned enemy {type} at {camId}");
|
||||
ClientEnemyManager.AddEnemyByTemplate(type, id, ClientMapManager.Get(camId));
|
||||
UIManager.UpdateCameras([camId]);
|
||||
}
|
||||
|
||||
private static void MoveEnemy(int id, int camId) {
|
||||
Console.WriteLine($"E: Enemy {id} moved to {camId}");
|
||||
int oldPos = ClientEnemyManager.Get(id).Location!.Id;
|
||||
ClientEnemyManager.Move(id, ClientMapManager.Get(camId));
|
||||
UIManager.UpdateCameras([oldPos, camId]);
|
||||
SoundManager.PlayEnemyMove(ClientEnemyManager.Get(id));
|
||||
}
|
||||
|
||||
private static void EnemyAttack(int enemyId, int pid) {
|
||||
Console.WriteLine($"E: Enemy {enemyId} attacked player {pid}");
|
||||
if (pid == Client.Player.State.Pid) {
|
||||
UIManager.Jumpscare(ClientEnemyManager.Get(pid));
|
||||
SoundManager.PlayJumpscare();
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnemyReset(int id, int camId) {
|
||||
Console.WriteLine($"E: Enemy {id} reset to {camId}");
|
||||
int preResetPos = ClientEnemyManager.Get(id).Location!.Id;
|
||||
ClientEnemyManager.Move(id, ClientMapManager.Get(camId));
|
||||
UIManager.UpdateCameras([preResetPos, camId]);
|
||||
SoundManager.PlayEnemyReset(ClientEnemyManager.Get(id));
|
||||
}
|
||||
|
||||
private static void SpotSetActive(int id, bool state) {
|
||||
ClientEnemy enemy = ClientEnemyManager.Get(id);
|
||||
if (enemy.Type != EnemyType.SPOT) return;
|
||||
Console.WriteLine($"E: Spot:{id} turned {(state ? "on" : " off")}");
|
||||
ClientEnemyManager.Get(id).Sprite.SetTexture(state ? 0 : 1);
|
||||
SoundManager.PlaySpotActivate();
|
||||
}
|
||||
|
||||
private static void PlayerWin(int pid) {
|
||||
Console.WriteLine($"E: Player {pid} won");
|
||||
if(Client.Player.State.Pid == pid) UIManager.ShowVictoryScreen();
|
||||
Client.Disconnect();
|
||||
ClientEnemyManager.ClearEnemies();
|
||||
}
|
||||
|
||||
private static void StartGame() {
|
||||
Console.WriteLine("E: Game started");
|
||||
UIManager.DisplayGameUI();
|
||||
UIManager.StartTimer();
|
||||
SoundManager.StartAmbience();
|
||||
Client.State = Client.ConnectionState.GAME_IN_PROGRESS;
|
||||
}
|
||||
|
||||
private static void PowerTick(int pid, int value) {
|
||||
// Console.WriteLine($"E: power tick {e.Args[0]}: {e.Args[1]}");
|
||||
if (pid == Client.Player.State.Pid){
|
||||
Client.Player.State.Power = value;
|
||||
}
|
||||
else{
|
||||
Client.Opponent.State.Power = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void PowerOut(int pid) {
|
||||
Console.WriteLine($"E: Player {pid} powered out");
|
||||
ClientMapManager.GetAllConnectors().Where(c =>
|
||||
(c.Type == ConnectorType.DOOR_REMOTE || c.Type == ConnectorType.DOOR_OFFICE) &&
|
||||
c.Owner == Client.GetPlayer(pid)).ToList().ForEach(c =>
|
||||
{
|
||||
c.Blocked = false;
|
||||
if(c.Type == ConnectorType.DOOR_REMOTE)
|
||||
UIManager.ChangeRemoteDoorState(c.Id, false);
|
||||
});
|
||||
foreach (var tile in ClientMapManager.GetAllTiles()){
|
||||
tile.Lit = false;
|
||||
}
|
||||
|
||||
if (pid == Client.Player.State.Pid){
|
||||
UIManager.ChangeDoorState(Direction.EAST, false);
|
||||
UIManager.ChangeDoorState(Direction.NORTH, false);
|
||||
UIManager.ChangeDoorState(Direction.WEST, false);
|
||||
CommandManager.AllowGameControls(false);
|
||||
UIManager.ChangeMonitorState(false);
|
||||
SoundManager.PlayPowerOut();
|
||||
}
|
||||
else{
|
||||
UIManager.ChangeDoorStateOpponent(Direction.EAST, false);
|
||||
UIManager.ChangeDoorStateOpponent(Direction.NORTH, false);
|
||||
UIManager.ChangeDoorStateOpponent(Direction.WEST, false);
|
||||
UIManager.ChangeMonitorStateOpponent(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ChangeLightState(int pid, int camId, bool state) {
|
||||
Console.WriteLine($"E: Player {pid} {(state ? "lit": "unlit")} tile {camId}");
|
||||
if (pid == Client.Player.State.Pid){
|
||||
if (ClientMapManager.Get(camId).Lit != state) Console.WriteLine("!!! DESYNC: LIGHT STATE");
|
||||
return;
|
||||
}
|
||||
|
||||
ClientMapManager.Get(camId).Lit = state;
|
||||
UIManager.UpdateCameras([camId]);
|
||||
}
|
||||
|
||||
private static void NekoAnger() {
|
||||
SoundManager.PlayNekoAnger();
|
||||
}
|
||||
|
||||
private static void VentWalk() {
|
||||
SoundManager.PlayVentWalk();
|
||||
}
|
||||
|
||||
private static void NextPhase() {
|
||||
SoundManager.PlayBell();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue