public async Task <int> StartNetworked(Func <string, bool> acceptPlayer, Action <string, int> handlePlayerExit, MonoBehaviour parent, DemoSettingsServer settings)
    {
        this.handlePlayerExit = handlePlayerExit;
        parent.StartCoroutine(Update());
        var spawnPoint = DemoSettings.GetSpawnPoint();

        var platformerConfig = DemoSettings.GetPlatformCharacterConfig();

        networkStream = await NetworkApi.AcceptInbound(parent, settings.port);

        networkStream.Recieve.Add((opCode, guid, message) => {
            if (opCode == OpCode.PLAYER_JOIN)
            {
                var joinMessage = (PlayerJoinMessage)message;
                acceptPlayer(joinMessage.playerSessionId);
                var onCancel = networkStream.Recieve.GetConnectionCancellation(guid);
                onCancel.Token.Register(() => {
                    lock (abandonedPlayers){
                        abandonedPlayers.Enqueue(guid);
                    }
                });
                playerGuids.Add(guid);
                var serverRep        = GameObject.Instantiate(settings.playerPrefab, spawnPoint, Quaternion.identity);
                var serverRepComp    = serverRep.GetComponent <PlayerCompServer>();
                var serverRepRecieve = serverRepComp.RegisterNetwork(guid, (toSendCode, toSendMessage) => {
                    playerGuids.ForEach((targetGuid) => {
                        try{
                            networkStream.Send(toSendCode, targetGuid, toSendMessage);
                        } catch (Exception e) {
                            Debug.Log("failed sending to player: " + e.Message);
                        }
                    });
                    return(1);
                }, platformerConfig, settings);
                ServerRepData repData   = new ServerRepData();
                repData.comp            = serverRepComp;
                repData.session         = joinMessage.playerSessionId;
                repData.recieve         = serverRepRecieve;
                repData.guid            = guid;
                serverReps[guid]        = repData;
                var acceptMsg           = new PlayerAcceptMessage();
                acceptMsg.trustDistance = settings.trustDistance;
                playerGuids.ForEach((existingPlayer) => {
                    acceptMsg.playerGuid         = guid;
                    var isSendingToNewlyAccepted = PlayerGuidUtil.IsFor(acceptMsg, existingPlayer);
                    acceptMsg.playerSessionId    = isSendingToNewlyAccepted ? joinMessage.playerSessionId : string.Empty;
                    networkStream.Send(OpCode.PLAYER_ACCEPTED, existingPlayer, acceptMsg);
                    if (!isSendingToNewlyAccepted)
                    {
                        acceptMsg.playerSessionId = string.Empty;
                        acceptMsg.playerGuid      = existingPlayer;
                        networkStream.Send(OpCode.PLAYER_ACCEPTED, guid, acceptMsg);
                    }
                });
            }
            else
            {
                if (serverReps.ContainsKey(guid))
                {
                    serverReps[guid].recieve(opCode, message);
                }
            }
            return(1);
        });
        return(1);
    }
    public MessageHandler RegisterNetwork(string guid, MessageHandler send, PlatformerCharacterConfig config, DemoSettingsServer settings)
    {
        var trustSquared      = settings.trustDistance * settings.trustDistance;
        var broadcastInputMsg = new PlayerInputMessage();

        broadcastInputMsg.playerGuid = guid;
        this.platformerConfig        = config;
        this.networkSend             = send;
        StartCoroutine(ServerUpdate(send, settings.tickRate, guid));
        return((opCode, message) => {
            if (opCode == OpCode.PLAYER_INPUT)
            {
                var updatedState = (PlayerInputMessage)message;
                updatedState.state.CopyTo(platformerInputState);
                var actualPosition = transform.position;
                var clientPosition = updatedState.position.toVector();
                var deltaPosition = actualPosition - clientPosition;
                if (deltaPosition.sqrMagnitude < trustSquared)
                {
                    transform.position = clientPosition;
                }
                else
                {
                    transform.position = (deltaPosition.normalized * settings.trustDistance) + actualPosition;
                }
                updatedState.state.CopyTo(broadcastInputMsg.state);
                broadcastInputMsg.position.FromVector(transform.position);
                send(OpCode.PLAYER_INPUT, broadcastInputMsg);
            }
            return 1;
        });
    }