From 9b0d7c10200a2adeeffb1463c76ff34151de4c3a Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Sat, 10 Jun 2017 15:07:10 +0300 Subject: [PATCH] Added a method for changing a client's controlled character. Clients now get back control if their character is revived via the debug console. + It's possible to give clients control of monsters, which should add some new gameplay possibilitie. ;) --- Barotrauma/Source/Characters/AICharacter.cs | 2 +- Barotrauma/Source/Characters/Character.cs | 8 +- .../Source/Characters/CharacterNetworking.cs | 62 +++++++++++--- Barotrauma/Source/DebugConsole.cs | 84 ++++++++++++++----- Barotrauma/Source/Networking/GameServer.cs | 34 +++++++- .../NetEntityEvent/NetEntityEvent.cs | 7 +- 6 files changed, 151 insertions(+), 46 deletions(-) diff --git a/Barotrauma/Source/Characters/AICharacter.cs b/Barotrauma/Source/Characters/AICharacter.cs index d4fcb7ca6..7a32bd5b6 100644 --- a/Barotrauma/Source/Characters/AICharacter.cs +++ b/Barotrauma/Source/Characters/AICharacter.cs @@ -27,7 +27,7 @@ namespace Barotrauma { base.Update(cam, deltaTime); - if (!Enabled) return; + if (!Enabled || IsRemotePlayer) return; float dist = Vector2.DistanceSquared(cam.WorldViewCenter, WorldPosition); if (dist > 8000.0f * 8000.0f) diff --git a/Barotrauma/Source/Characters/Character.cs b/Barotrauma/Source/Characters/Character.cs index b76febfd4..0e133f1fa 100644 --- a/Barotrauma/Source/Characters/Character.cs +++ b/Barotrauma/Source/Characters/Character.cs @@ -64,7 +64,7 @@ namespace Barotrauma public Hull PreviousHull = null; public Hull CurrentHull = null; - public readonly bool IsRemotePlayer; + public bool IsRemotePlayer; private CharacterInventory inventory; @@ -810,7 +810,7 @@ namespace Barotrauma ViewTarget = null; if (!AllowInput) return; - if (!(this is AICharacter) || controlled == this) + if (!(this is AICharacter) || controlled == this || IsRemotePlayer) { Vector2 targetMovement = GetTargetMovement(); @@ -1289,7 +1289,7 @@ namespace Barotrauma { foreach (Character c in CharacterList) { - if (!(c is AICharacter)) continue; + if (!(c is AICharacter) && !c.IsRemotePlayer) continue; if (GameMain.Server != null) { @@ -1432,7 +1432,7 @@ namespace Barotrauma selectedConstruction = null; } - if (controlled != this && !(this is AICharacter)) + if (controlled != this && (!(this is AICharacter) || IsRemotePlayer)) { if (!LockHands) { diff --git a/Barotrauma/Source/Characters/CharacterNetworking.cs b/Barotrauma/Source/Characters/CharacterNetworking.cs index 2787e1164..0f52f1c80 100644 --- a/Barotrauma/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/Source/Characters/CharacterNetworking.cs @@ -82,7 +82,7 @@ namespace Barotrauma private bool networkUpdateSent; public bool isSynced = false; - + public List MemState { get { return memState; } @@ -93,6 +93,16 @@ namespace Barotrauma get { return memLocalState; } } + public void ResetNetState() + { + memInput.Clear(); + memState.Clear(); + memLocalState.Clear(); + + LastNetworkUpdateID = 0; + LastProcessedID = 0; + } + private void UpdateNetInput() { if (this != Character.Controlled) @@ -112,7 +122,7 @@ namespace Barotrauma } } } - else if (GameMain.Server != null && !(this is AICharacter)) + else if (GameMain.Server != null && (!(this is AICharacter) || IsRemotePlayer)) { if (!AllowInput) { @@ -387,17 +397,24 @@ namespace Barotrauma if (extraData != null) { + switch ((NetEntityEvent.Type)extraData[0]) { case NetEntityEvent.Type.InventoryState: - msg.Write(true); + msg.WriteRangedInteger(0, 2, 0); inventory.ClientWrite(msg, extraData); break; + case NetEntityEvent.Type.Control: + msg.WriteRangedInteger(0, 2, 1); + Client owner = ((Client)extraData[1]); + msg.Write(owner == null ? (byte)0 : owner.ID); + break; case NetEntityEvent.Type.Status: - msg.Write(false); + msg.WriteRangedInteger(0, 2, 2); WriteStatus(msg); break; } + msg.WritePadBits(); } else { @@ -573,15 +590,36 @@ namespace Barotrauma break; case ServerNetObject.ENTITY_EVENT: - bool isInventoryUpdate = msg.ReadBoolean(); + + int eventType = msg.ReadRangedInteger(0, 2); + switch (eventType) + { + case 0: + inventory.ClientRead(type, msg, sendingTime); + break; + case 1: + byte ownerID = msg.ReadByte(); + ResetNetState(); + if (ownerID == GameMain.Client.ID) + { + if (controlled != null) + { + LastNetworkUpdateID = controlled.LastNetworkUpdateID; + } - if (isInventoryUpdate) - { - inventory.ClientRead(type, msg, sendingTime); - } - else - { - ReadStatus(msg); + controlled = this; + IsRemotePlayer = false; + GameMain.Client.Character = this; + } + else if (controlled == this) + { + controlled = null; + IsRemotePlayer = ownerID > 0; + } + break; + case 2: + ReadStatus(msg); + break; } break; diff --git a/Barotrauma/Source/DebugConsole.cs b/Barotrauma/Source/DebugConsole.cs index 893364235..288d04a4a 100644 --- a/Barotrauma/Source/DebugConsole.cs +++ b/Barotrauma/Source/DebugConsole.cs @@ -404,17 +404,19 @@ namespace Barotrauma break; case "banip": - if (GameMain.Server == null || commands.Length < 2) break; + { + if (GameMain.Server == null || commands.Length < 2) break; - var client = GameMain.Server.ConnectedClients.Find(c => c.Connection.RemoteEndPoint.Address.ToString() == commands[1]); - if (client == null) - { - GameMain.Server.BanList.BanPlayer("Unnamed", commands[1]); - } - else - { - //GameMain.Server.KickClient(client, true); - } + var client = GameMain.Server.ConnectedClients.Find(c => c.Connection.RemoteEndPoint.Address.ToString() == commands[1]); + if (client == null) + { + GameMain.Server.BanList.BanPlayer("Unnamed", commands[1]); + } + else + { + GameMain.Server.KickClient(client, true); + } + } break; case "startclient": if (commands.Length == 1) return; @@ -462,15 +464,43 @@ namespace Barotrauma break; case "controlcharacter": case "control": - if (commands.Length < 2) break; - - var character = FindMatchingCharacter(commands, true); - - if (character != null) { - Character.Controlled = character; + if (commands.Length < 2) break; + + var character = FindMatchingCharacter(commands, true); + + if (character != null) + { + Character.Controlled = character; + } + } + break; + case "setclientcharacter": + { + if (GameMain.Server == null) break; + + int separatorIndex = Array.IndexOf(commands, ";"); + + if (separatorIndex == -1 || commands.Length < 4) + { + ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] ; [character]\""); + break; + } + + string[] commandsLeft = commands.Take(separatorIndex).ToArray(); + string[] commandsRight = commands.Skip(separatorIndex).ToArray(); + + string clientName = String.Join(" ", commandsLeft.Skip(1)); + + var client = GameMain.Server.ConnectedClients.Find(c => c.name == clientName); + if (client == null) + { + ThrowError("Client \"" + clientName + "\" not found."); + } + + var character = FindMatchingCharacter(commandsRight, false); + GameMain.Server.SetClientCharacter(client, character); } - break; case "teleportcharacter": case "teleport": @@ -533,19 +563,29 @@ namespace Barotrauma break; case "revive": - Character reveivedCharacter = null; + Character revivedCharacter = null; if (commands.Length == 1) { - reveivedCharacter = Character.Controlled; + revivedCharacter = Character.Controlled; } else { - reveivedCharacter = FindMatchingCharacter(commands); + revivedCharacter = FindMatchingCharacter(commands); } - if (reveivedCharacter != null) + if (revivedCharacter != null) { - reveivedCharacter.Revive(false); + revivedCharacter.Revive(false); + if (GameMain.Server != null) + { + foreach (Client c in GameMain.Server.ConnectedClients) + { + if (c.Character != revivedCharacter) continue; + //clients stop controlling the character when it dies, force control back + GameMain.Server.SetClientCharacter(c, revivedCharacter); + break; + } + } } break; case "freeze": diff --git a/Barotrauma/Source/Networking/GameServer.cs b/Barotrauma/Source/Networking/GameServer.cs index e78821349..766d5a11d 100644 --- a/Barotrauma/Source/Networking/GameServer.cs +++ b/Barotrauma/Source/Networking/GameServer.cs @@ -1,5 +1,4 @@ - -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -1945,6 +1944,37 @@ namespace Barotrauma.Networking return true; } + public void SetClientCharacter(Client client, Character newCharacter) + { + if (client == null) return; + + //the client's previous character is no longer a remote player + if (client.Character != null) + { + client.Character.IsRemotePlayer = false; + } + + if (newCharacter == null) + { + if (client.Character != null) //removing control of the current character + { + newCharacter.IsRemotePlayer = false; + CreateEntityEvent(client.Character, new object[] { NetEntityEvent.Type.Control, null }); + client.Character = null; + } + + } + else if (client.Character != newCharacter) //taking control of a new character + { + newCharacter.ResetNetState(); + newCharacter.LastNetworkUpdateID = client.Character.LastNetworkUpdateID; + + newCharacter.IsRemotePlayer = true; + client.Character = newCharacter; + CreateEntityEvent(newCharacter, new object[] { NetEntityEvent.Type.Control, client }); + } + } + private void UpdateCharacterInfo(NetIncomingMessage message, Client sender) { Gender gender = Gender.Male; diff --git a/Barotrauma/Source/Networking/NetEntityEvent/NetEntityEvent.cs b/Barotrauma/Source/Networking/NetEntityEvent/NetEntityEvent.cs index c0cb081df..7d304601e 100644 --- a/Barotrauma/Source/Networking/NetEntityEvent/NetEntityEvent.cs +++ b/Barotrauma/Source/Networking/NetEntityEvent/NetEntityEvent.cs @@ -1,9 +1,5 @@ using Lidgren.Network; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Barotrauma.Networking { @@ -16,7 +12,8 @@ namespace Barotrauma.Networking Status, Repair, ApplyStatusEffect, - ChangeProperty + ChangeProperty, + Control } public readonly Entity Entity;