private void doPlayerReconciliation(ClientPlayer player, NetworkingData.PlayerStateData playerState)
    {
        var reconciledPosition = playerState.Position;

        var max = player.reconciliationInputs.Count;

        if (player.reconciliationInputs.Count > 100)
        {
            Debug.LogError($"We seem to be leaking reconciliation inputs (count: {player.reconciliationInputs.Count}");
        }

        for (int x = 0; x < max; x++)
        {
            var nextInput = player.reconciliationInputs.Dequeue();

            if (nextInput.InputSeq >= playerState.LastProcessedInput)
            {
                reconciledPosition = PlayerMovement.MovePlayer(nextInput, reconciledPosition, nextInput.DeltaTime);
            }

            var renderPosition = player.transform.localPosition;
            if (Vector2.Distance(new Vector2(renderPosition.x, renderPosition.y), reconciledPosition) > 0.05f)
            {
                //TODO may want to really, really re-verify this reconciliation logic isn't causing issues/popping/choppy rendering
                //TODO remove me Debug.Log($"reconciling position from rendered position: {renderPosition.x}, {renderPosition.y} to server position: {reconciledPosition.X}, {reconciledPosition.Y}");
                player.transform.localPosition = new Vector3(reconciledPosition.X, reconciledPosition.Y, 0);
            }
        }
    }
        static void SendPlayerStateDataUpdates()
        {
            var players = Server.Instance.Players.Values.ToArray();

            NetworkingData.PlayerStateData[] positions = new NetworkingData.PlayerStateData[players.Length];

            int i = 0;

            foreach (var player in players)
            {
                positions[i] = player.getStateData();
                i++;
            }

            foreach (KeyValuePair <ushort, Player> p in Server.Instance.Players)
            {
                NetworkingData.GameUpdateData data = new NetworkingData.GameUpdateData(positions);
                using (Message m = Message.Create((ushort)NetworkingData.Tags.GameUpdate, data))
                {
                    p.Value.SendMessage(m, SendMode.Reliable);
                }
            }
        }
    private void processServerUpdates()
    {
        //TODO can we clean up the deep nesting here?
        if (worldUpdateBuffer.Count > 0)
        {
            int numUpdates = worldUpdateBuffer.Count;
            for (int i = 0; i < numUpdates; i++)
            {
                NetworkingData.GameUpdateData nextUpdate = worldUpdateBuffer.Dequeue();

                for (int j = 0; j < nextUpdate.UpdateData.Length; j++)
                {
                    NetworkingData.PlayerStateData playerState = nextUpdate.UpdateData[j];

                    ClientPlayer player;
                    if (players.TryGetValue(playerState.Id, out player))
                    {
                        if (player.isOwn)
                        {
                            //server position for us is used for reconciliation
                            doPlayerReconciliation(player, playerState);
                        }
                        else
                        {
                            //server position for others is authoritative (enqueue in positionBuffer since we smooth this with interpolation later)
                            playerState.LocalRenderTimestamp = Time.time * 1000f;
                            player.positionBuffer.Enqueue(playerState);
                        }
                    }
                    else
                    {
                        Debug.Log($"ERROR got update for player (id:{playerState.Id}, {playerState.Position.X}, {playerState.Position.Y}) that we don't know about!");
                    }
                }
            }
        }
    }