Basic player input syncing
There's currently a bug where the inputs might be read out of order or more than once, which leads to desync. I'll feel really dumb when I find what's going on here. Also, I'm using placeholder player spawning because CharacterInfo doesn't seem to be fully functional yet.
This commit is contained in:
@@ -702,9 +702,9 @@ namespace Barotrauma
|
||||
}
|
||||
if (isFrozen)
|
||||
{
|
||||
for (int i=0;i < Limbs.Length;i++)
|
||||
foreach (Limb l in Limbs)
|
||||
{
|
||||
Limbs[i].body.PhysEnabled = true;
|
||||
l.body.PhysEnabled = true;
|
||||
}
|
||||
isFrozen = false;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Barotrauma
|
||||
get { return netStateID; }
|
||||
}
|
||||
|
||||
byte dequeuedInput = 0; byte prevDequeuedInput = 0;
|
||||
List<byte> memInput = new List<byte>();
|
||||
List<float> memMouseX = new List<float>();
|
||||
List<float> memMouseY = new List<float>();
|
||||
@@ -74,7 +75,7 @@ namespace Barotrauma
|
||||
|
||||
private CharacterInventory inventory;
|
||||
|
||||
public float LastNetworkUpdate;
|
||||
private UInt32 LastNetworkUpdateID = 0;
|
||||
|
||||
//public int LargeUpdateTimer;
|
||||
|
||||
@@ -638,11 +639,54 @@ namespace Barotrauma
|
||||
|
||||
public bool IsKeyHit(InputType inputType)
|
||||
{
|
||||
if (GameMain.Server != null && Character.Controlled != null)
|
||||
{
|
||||
switch (inputType)
|
||||
{
|
||||
case InputType.Left:
|
||||
return ((dequeuedInput & 1) > 0) && !((prevDequeuedInput & 1) > 0);
|
||||
//break;
|
||||
case InputType.Right:
|
||||
return ((dequeuedInput & 2) > 0) && !((prevDequeuedInput & 2) > 0);
|
||||
//break;
|
||||
case InputType.Up:
|
||||
return ((dequeuedInput & 4) > 0) && !((prevDequeuedInput & 4) > 0);
|
||||
//break;
|
||||
case InputType.Down:
|
||||
return ((dequeuedInput & 8) > 0) && !((prevDequeuedInput & 8) > 0);
|
||||
//break;
|
||||
default:
|
||||
return false;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
return keys[(int)inputType].Hit;
|
||||
}
|
||||
|
||||
public bool IsKeyDown(InputType inputType)
|
||||
{
|
||||
if (GameMain.Server!=null && Character.Controlled!=this)
|
||||
{
|
||||
bool retVal = false;
|
||||
switch (inputType)
|
||||
{
|
||||
case InputType.Left:
|
||||
retVal = (dequeuedInput & 1) > 0;
|
||||
break;
|
||||
case InputType.Right:
|
||||
retVal = (dequeuedInput & 2) > 0;
|
||||
break;
|
||||
case InputType.Up:
|
||||
retVal = (dequeuedInput & 4) > 0;
|
||||
break;
|
||||
case InputType.Down:
|
||||
retVal = (dequeuedInput & 8) > 0;
|
||||
break;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return keys[(int)inputType].Held;
|
||||
}
|
||||
|
||||
@@ -1146,7 +1190,38 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
|
||||
if (!IsDead)
|
||||
{
|
||||
if (memInput.Count > 0)
|
||||
{
|
||||
AnimController.Frozen = false;
|
||||
prevDequeuedInput = dequeuedInput;
|
||||
dequeuedInput = memInput[memInput.Count - 1];
|
||||
memInput.RemoveAt(memInput.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimController.Frozen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
byte newInput = 0;
|
||||
newInput |= IsKeyDown(InputType.Left) ? (byte)1 : (byte)0;
|
||||
newInput |= IsKeyDown(InputType.Right) ? (byte)2 : (byte)0;
|
||||
newInput |= IsKeyDown(InputType.Up) ? (byte)4 : (byte)0;
|
||||
newInput |= IsKeyDown(InputType.Down) ? (byte)8 : (byte)0;
|
||||
memInput.Insert(0,newInput);
|
||||
LastNetworkUpdateID++;
|
||||
while (memInput.Count>60)
|
||||
{
|
||||
memInput.RemoveAt(memInput.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if (networkUpdateSent)
|
||||
@@ -1585,12 +1660,48 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public virtual void ClientWrite(NetOutgoingMessage msg)
|
||||
{
|
||||
//TODO: write inputs
|
||||
{
|
||||
if (GameMain.Server != null) return;
|
||||
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
|
||||
|
||||
while (memInput.Count > 60)
|
||||
{
|
||||
memInput.RemoveAt(memInput.Count - 1);
|
||||
}
|
||||
|
||||
msg.Write(LastNetworkUpdateID);
|
||||
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
|
||||
msg.Write(inputCount);
|
||||
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
msg.Write(memInput[i]);
|
||||
}
|
||||
}
|
||||
public virtual void ServerRead(NetIncomingMessage msg, Client c)
|
||||
{
|
||||
//TODO: read inputs
|
||||
{
|
||||
if (GameMain.Server == null) return;
|
||||
|
||||
UInt32 networkUpdateID = msg.ReadUInt32();
|
||||
byte inputCount = msg.ReadByte();
|
||||
|
||||
for (int i=0;i<inputCount;i++)
|
||||
{
|
||||
byte newInput = msg.ReadByte();
|
||||
if ((i < (networkUpdateID-LastNetworkUpdateID)) && (i<60))
|
||||
{
|
||||
memInput.Insert(0, newInput);
|
||||
}
|
||||
}
|
||||
if (networkUpdateID > LastNetworkUpdateID)
|
||||
{
|
||||
LastNetworkUpdateID = networkUpdateID;
|
||||
}
|
||||
while (memInput.Count > 60)
|
||||
{
|
||||
//deleting inputs from the queue here means the server is way behind and data needs to be dropped
|
||||
memInput.RemoveAt(memInput.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ServerWrite(NetOutgoingMessage msg, Client c)
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Barotrauma.Networking
|
||||
public NetConnection Connection { get; set; }
|
||||
public string version;
|
||||
public bool inGame;
|
||||
public UInt32 lastRecvLobbyUpdate = 0;
|
||||
public UInt32 lastRecvGeneralUpdate = 0;
|
||||
|
||||
public bool hasLobbyData = false;
|
||||
public UInt32 lastSentChatMsgID = 0; //last msg this client said
|
||||
|
||||
@@ -485,6 +485,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
SendLobbyUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
SendIngameUpdate();
|
||||
}
|
||||
|
||||
// Update current time
|
||||
updateTimer = DateTime.Now + updateInterval;
|
||||
@@ -513,6 +517,9 @@ namespace Barotrauma.Networking
|
||||
case ServerPacketHeader.UPDATE_LOBBY:
|
||||
ReadLobbyUpdate(inc);
|
||||
break;
|
||||
case ServerPacketHeader.UPDATE_INGAME:
|
||||
ReadIngameUpdate(inc);
|
||||
break;
|
||||
case ServerPacketHeader.QUERY_STARTGAME:
|
||||
string subName = inc.ReadString();
|
||||
string subHash = inc.ReadString();
|
||||
@@ -560,6 +567,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
bool respawnAllowed = inc.ReadBoolean();
|
||||
|
||||
float posX = inc.ReadFloat();
|
||||
float posY = inc.ReadFloat();
|
||||
|
||||
GameModePreset gameMode = GameModePreset.list.Find(gm => gm.Name == modeName);
|
||||
|
||||
if (gameMode == null)
|
||||
@@ -591,6 +601,10 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
DebugConsole.NewMessage(Convert.ToString(posX) + "," + Convert.ToString(posY), Color.Lime);
|
||||
Character myChar = Character.Create(Character.HumanConfigFile, new Vector2(posX, posY), null, true, false);
|
||||
Character.Controlled = myChar;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
@@ -661,6 +675,32 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadIngameUpdate(NetIncomingMessage inc)
|
||||
{
|
||||
ServerNetObject objHeader;
|
||||
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
|
||||
{
|
||||
switch (objHeader)
|
||||
{
|
||||
case ServerNetObject.SYNC_IDS:
|
||||
lastSentChatMsgID = inc.ReadUInt32();
|
||||
break;
|
||||
case ServerNetObject.CHARACTER_POSITION:
|
||||
bool dead = inc.ReadBoolean();
|
||||
inc.ReadPadBits();
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
if (dead && !Character.Controlled.IsDead)
|
||||
Character.Controlled.Kill(CauseOfDeath.Damage);
|
||||
}
|
||||
break;
|
||||
case ServerNetObject.CHAT_MESSAGE:
|
||||
ChatMessage.ClientRead(inc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendLobbyUpdate()
|
||||
{
|
||||
NetOutgoingMessage outmsg = client.CreateMessage();
|
||||
@@ -683,6 +723,34 @@ namespace Barotrauma.Networking
|
||||
client.SendMessage(outmsg, NetDeliveryMethod.Unreliable);
|
||||
}
|
||||
|
||||
private void SendIngameUpdate()
|
||||
{
|
||||
NetOutgoingMessage outmsg = client.CreateMessage();
|
||||
outmsg.Write((byte)ClientPacketHeader.UPDATE_INGAME);
|
||||
|
||||
outmsg.Write((byte)ClientNetObject.SYNC_IDS);
|
||||
outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.Write(ChatMessage.LastID);
|
||||
ChatMessage removeMsg;
|
||||
while ((removeMsg = chatMsgQueue.Find(cMsg => cMsg.NetStateID <= lastSentChatMsgID)) != null)
|
||||
{
|
||||
chatMsgQueue.Remove(removeMsg);
|
||||
}
|
||||
|
||||
foreach (ChatMessage cMsg in chatMsgQueue)
|
||||
{
|
||||
cMsg.ClientWrite(outmsg);
|
||||
}
|
||||
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.ClientWrite(outmsg);
|
||||
}
|
||||
|
||||
outmsg.Write((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
client.SendMessage(outmsg, NetDeliveryMethod.Unreliable);
|
||||
}
|
||||
|
||||
public override void SendChatMessage(string message, ChatMessageType? type = null)
|
||||
{
|
||||
if (client.ServerConnection == null) return;
|
||||
|
||||
@@ -505,7 +505,7 @@ namespace Barotrauma.Networking
|
||||
ClientReadLobby(inc);
|
||||
break;
|
||||
case ClientPacketHeader.UPDATE_INGAME:
|
||||
//TODO
|
||||
ClientReadIngame(inc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -558,7 +558,7 @@ namespace Barotrauma.Networking
|
||||
inc.SenderConnection.Disconnect("You're not a connected client.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ClientNetObject objHeader;
|
||||
while ((objHeader=(ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE)
|
||||
{
|
||||
@@ -566,10 +566,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case ClientNetObject.SYNC_IDS:
|
||||
//TODO: might want to use a clever class for this
|
||||
UInt32 lastLobbyUpdID = inc.ReadUInt32();
|
||||
if (lastLobbyUpdID > c.lastRecvLobbyUpdate)
|
||||
UInt32 lastGeneralUpdID = inc.ReadUInt32();
|
||||
if (lastGeneralUpdID > c.lastRecvGeneralUpdate)
|
||||
{
|
||||
c.lastRecvLobbyUpdate = lastLobbyUpdID;
|
||||
c.lastRecvGeneralUpdate = lastGeneralUpdID;
|
||||
}
|
||||
UInt32 lastChatID = inc.ReadUInt32();
|
||||
if (lastChatID > c.lastRecvChatMsgID)
|
||||
@@ -580,6 +580,9 @@ namespace Barotrauma.Networking
|
||||
case ClientNetObject.CHAT_MESSAGE:
|
||||
ChatMessage.ServerRead(inc, c);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,18 +596,78 @@ namespace Barotrauma.Networking
|
||||
inc.SenderConnection.Disconnect("You're not a connected client.");
|
||||
return;
|
||||
}
|
||||
|
||||
ClientNetObject objHeader;
|
||||
while ((objHeader = (ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE)
|
||||
{
|
||||
switch (objHeader)
|
||||
{
|
||||
case ClientNetObject.SYNC_IDS:
|
||||
//TODO: might want to use a clever class for this
|
||||
UInt32 lastGeneralUpdID = inc.ReadUInt32();
|
||||
if (lastGeneralUpdID > c.lastRecvGeneralUpdate)
|
||||
{
|
||||
c.lastRecvGeneralUpdate = lastGeneralUpdID;
|
||||
}
|
||||
UInt32 lastChatID = inc.ReadUInt32();
|
||||
if (lastChatID > c.lastRecvChatMsgID)
|
||||
{
|
||||
c.lastRecvChatMsgID = lastChatID;
|
||||
}
|
||||
break;
|
||||
case ClientNetObject.CHAT_MESSAGE:
|
||||
ChatMessage.ServerRead(inc, c);
|
||||
break;
|
||||
case ClientNetObject.CHARACTER_INPUT:
|
||||
if (c.Character != null && !c.Character.IsDead && !c.Character.IsUnconscious)
|
||||
{
|
||||
c.Character.ServerRead(inc, c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClientWriteIngame(Client c)
|
||||
{
|
||||
NetOutgoingMessage outmsg = server.CreateMessage();
|
||||
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
|
||||
outmsg.Write(c.lastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
|
||||
|
||||
foreach (GUIComponent gc in GameMain.NetLobbyScreen.ChatBox.children)
|
||||
{
|
||||
if (gc is GUITextBlock)
|
||||
{
|
||||
if (gc.UserData is ChatMessage)
|
||||
{
|
||||
ChatMessage cMsg = (ChatMessage)gc.UserData;
|
||||
if (cMsg.NetStateID > c.lastRecvChatMsgID)
|
||||
{
|
||||
cMsg.ServerWrite(outmsg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.CHARACTER_POSITION);
|
||||
if (c.Character != null && !c.Character.IsDead)
|
||||
{
|
||||
|
||||
outmsg.Write(false); //not dead
|
||||
outmsg.WritePadBits();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
outmsg.Write(true); //dead
|
||||
outmsg.WritePadBits();
|
||||
}
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
|
||||
}
|
||||
|
||||
private void ClientWriteLobby(Client c)
|
||||
@@ -614,7 +677,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
|
||||
|
||||
if (c.lastRecvLobbyUpdate<GameMain.NetLobbyScreen.LastUpdateID)
|
||||
if (c.lastRecvGeneralUpdate<GameMain.NetLobbyScreen.LastUpdateID)
|
||||
{
|
||||
outmsg.Write(true);
|
||||
outmsg.WritePadBits();
|
||||
@@ -622,7 +685,7 @@ namespace Barotrauma.Networking
|
||||
outmsg.Write(GameMain.NetLobbyScreen.GetServerName());
|
||||
outmsg.Write(GameMain.NetLobbyScreen.ServerMessage);
|
||||
var subList = GameMain.NetLobbyScreen.GetSubList();
|
||||
if (c.lastRecvLobbyUpdate < 1)
|
||||
if (c.lastRecvGeneralUpdate < 1)
|
||||
{
|
||||
outmsg.Write((UInt16)subList.Count);
|
||||
for (int i = 0; i < subList.Count; i++)
|
||||
@@ -776,8 +839,24 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (AllowRespawn) respawnManager = new RespawnManager(this, selectedShuttle);
|
||||
|
||||
var startMessage = CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset);
|
||||
server.SendMessage(startMessage, connectedClients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0);
|
||||
List<CharacterInfo> characterInfos = new List<CharacterInfo>();
|
||||
foreach (Client c in connectedClients)
|
||||
{
|
||||
c.inGame = true;
|
||||
|
||||
WayPoint spawnPoint = WayPoint.GetRandom(SpawnType.Human);
|
||||
Vector2 spawnPosition = spawnPoint.WorldPosition;
|
||||
|
||||
DebugConsole.NewMessage(Convert.ToString(spawnPosition.X) + "," + Convert.ToString(spawnPosition.Y), Color.Lime);
|
||||
Character spawnedCharacter = Character.Create(Character.HumanConfigFile, spawnPosition, null, true, false);
|
||||
c.Character = spawnedCharacter;
|
||||
|
||||
GameMain.GameSession.CrewManager.characters.Add(c.Character);
|
||||
}
|
||||
|
||||
CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset, connectedClients);
|
||||
//var startMessage = CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset);
|
||||
//server.SendMessage(startMessage, connectedClients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0);
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
@@ -802,28 +881,34 @@ namespace Barotrauma.Networking
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private NetOutgoingMessage CreateStartMessage(int seed, Submarine selectedSub, GameModePreset selectedMode)
|
||||
private void CreateStartMessage(int seed, Submarine selectedSub, GameModePreset selectedMode, List<Client> clients)
|
||||
{
|
||||
NetOutgoingMessage msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.STARTGAME);
|
||||
foreach (Client c in clients)
|
||||
{
|
||||
NetOutgoingMessage msg = server.CreateMessage();
|
||||
msg.Write((byte)ServerPacketHeader.STARTGAME);
|
||||
|
||||
msg.Write(seed);
|
||||
msg.Write(seed);
|
||||
|
||||
msg.Write(GameMain.NetLobbyScreen.LevelSeed);
|
||||
msg.Write(GameMain.NetLobbyScreen.LevelSeed);
|
||||
|
||||
msg.Write((byte)GameMain.NetLobbyScreen.MissionTypeIndex);
|
||||
msg.Write((byte)GameMain.NetLobbyScreen.MissionTypeIndex);
|
||||
|
||||
msg.Write(selectedSub.Name);
|
||||
msg.Write(selectedSub.MD5Hash.Hash);
|
||||
msg.Write(selectedSub.Name);
|
||||
msg.Write(selectedSub.MD5Hash.Hash);
|
||||
|
||||
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.Name);
|
||||
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash);
|
||||
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.Name);
|
||||
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash);
|
||||
|
||||
msg.Write(selectedMode.Name);
|
||||
msg.Write(selectedMode.Name);
|
||||
|
||||
msg.Write(AllowRespawn);
|
||||
|
||||
return msg;
|
||||
msg.Write(AllowRespawn);
|
||||
|
||||
msg.Write(c.Character.WorldPosition.X);
|
||||
msg.Write(c.Character.WorldPosition.Y);
|
||||
|
||||
c.Connection.SendMessage(msg, NetDeliveryMethod.ReliableUnordered,0);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndGame()
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma.Networking
|
||||
REQUEST_AUTH, //ask the server if a password is needed, if so we'll get nonce for encryption
|
||||
REQUEST_INIT, //ask the server to give you initialization
|
||||
UPDATE_LOBBY, //update state in lobby
|
||||
UPDATE_INGAME, //update state ingame while alive
|
||||
UPDATE_INGAME, //update state ingame
|
||||
|
||||
RESPONSE_STARTGAME //tell the server whether you're ready to start
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Networking
|
||||
AUTH_RESPONSE, //tell the player if they require a password to log in
|
||||
AUTH_FAILURE, //the server won't authorize player yet, however connection is still alive
|
||||
UPDATE_LOBBY, //update state in lobby (votes and chat messages)
|
||||
UPDATE_INGAME, //update state ingame while alive (character input and chat messages)
|
||||
UPDATE_INGAME, //update state ingame (character input and chat messages)
|
||||
|
||||
QUERY_STARTGAME, //ask the clients whether they're ready to start
|
||||
STARTGAME //start a new round
|
||||
|
||||
Reference in New Issue
Block a user