예제 #1
0
        internal void Tick()
        {
            // Pump new tasks
            var message = _messageQueue.GetMessage();

            if (message != null)
            {
                try
                {
                    message.Execute();
                }
                catch (Exception e)
                {
                    OnUnhandledException(new UnhandledExceptionEventArgs("async", e));
                }
            }

            try
            {
                _gameModeProvider.Tick();
            }
            catch (Exception e)
            {
                OnUnhandledException(new UnhandledExceptionEventArgs("Tick", e));
            }
        }
        private void ProcessCommand(ServerCommandData data)
        {
            switch (data.Command)
            {
            case ServerCommand.Nop:
                break;

            case ServerCommand.Tick:
                // The server expects at least a message every 5 seconds or else the debug pause
                // detector kicks in. Send one at least every 3 to be safe.
                if (DateTime.UtcNow - _lastSend > TimeSpan.FromSeconds(3))
                {
                    Send(ServerCommand.Alive, null);
                }

                if (!_canTick)
                {
                    break;
                }

                try
                {
                    _gameModeProvider.Tick();
                }
                catch (Exception e)
                {
                    OnUnhandledException(new UnhandledExceptionEventArgs("Tick", e));
                }

                break;

            case ServerCommand.Pong:
                if (_pongs.Count == 0)
                {
                    CoreLog.Log(CoreLogLevel.Error, "Received a random pong");
                    CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                }
                else
                {
                    _pongs.Dequeue().Pong();
                }
                break;

            case ServerCommand.Announce:
                CoreLog.Log(CoreLogLevel.Error, "Received a random server announcement");
                CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                break;

            case ServerCommand.PublicCall:
                var name   = ValueConverter.ToString(data.Data, 0, Encoding);
                var isInit = name == "OnGameModeInit";

                if (CoreLog.DoesLog(CoreLogLevel.Verbose))
                {
                    CoreLog.LogVerbose("Incoming public call: {0}", name);
                }

                if ((_startBehaviour == GameModeStartBehaviour.Gmx || _startBehaviour == GameModeStartBehaviour.FakeGmx) && !_initReceived &&
                    !isInit)
                {
                    CoreLog.Log(CoreLogLevel.Debug, $"Skipping callback {name} because OnGameModeInit has not yet been called");
                    Send(ServerCommand.Response, AZero);
                    break;
                }

                var isFirstInit = isInit && !_initReceived;
                if (isFirstInit)
                {
                    _initReceived = true;
                }

                if (_callbacks.TryGetValue(name, out var callback))
                {
                    int?result = null;
                    try
                    {
                        result = callback.Invoke(data.Data, name.Length + 1);

                        CoreLog.LogVerbose("Public call response for {0}: {1}", name, result);
                    }
                    catch (Exception e)
                    {
                        OnUnhandledException(new UnhandledExceptionEventArgs(name, e));
                    }

                    Send(ServerCommand.Response,
                         result != null
                                ? AOne.Concat(ValueConverter.GetBytes(result.Value))
                                : AZero);
                }
                else
                {
                    CoreLog.Log(CoreLogLevel.Error, "Received unknown callback " + name);
                    CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                }

                if (isFirstInit)
                {
                    if (_startBehaviour == GameModeStartBehaviour.FakeGmx)
                    {
                        FakeGmxRotate();
                    }

                    _canTick = true;
                }
                else if (_initReceived && name == "OnGameModeExit")
                {
                    CoreLog.Log(CoreLogLevel.Info, "OnGameModeExit received, sending reconnect signal...");
                    Send(ServerCommand.Reconnect, null);

                    // Give the server time to receive the reconnect signal.
                    Thread.Sleep(100);

                    CleanUp();
                }
                break;

            default:
                CoreLog.Log(CoreLogLevel.Error, $"Unknown command {data.Command} received with {data.Data?.Length.ToString() ?? "NULL"} data");
                CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                break;
            }
        }
예제 #3
0
        private void ProcessCommand(ServerCommandData data)
        {
            switch (data.Command)
            {
            case ServerCommand.Tick:
                if (!_canTick)
                {
                    break;
                }

                _gameModeProvider.Tick();

                // The server expects at least a message every 5 seconds or else the debug pause
                // detector kicks in. Send one at least every 3 to be safe.
                if (DateTime.UtcNow - _lastSend > TimeSpan.FromSeconds(3))
                {
                    Send(ServerCommand.Alive, null);
                }

                break;

            case ServerCommand.Pong:
                if (_pongs.Count == 0)
                {
                    CoreLog.Log(CoreLogLevel.Error, "Received a random pong");
                    CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                }
                else
                {
                    _pongs.Dequeue().Pong();
                }
                break;

            case ServerCommand.Announce:
                CoreLog.Log(CoreLogLevel.Error, "Received a random server announcement");
                CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                break;

            case ServerCommand.PublicCall:
                var name   = ValueConverter.ToString(data.Data, 0, Encoding);
                var isInit = name == "OnGameModeInit";

                if (CoreLog.DoesLog(CoreLogLevel.Verbose))
                {
                    CoreLog.LogVerbose("Incoming public call: {0}", name);
                }

                if ((_startBehaviour == GameModeStartBehaviour.Gmx || _startBehaviour == GameModeStartBehaviour.FakeGmx) && !_initReceived &&
                    !isInit)
                {
                    CoreLog.Log(CoreLogLevel.Debug, $"Skipping callback {name} because OnGameModeInit has not yet been called");
                    Send(ServerCommand.Response, AZero);
                    break;
                }

                var isFirstInit = isInit && !_initReceived;
                if (isFirstInit)
                {
                    _initReceived = true;
                }

                if (_callbacks.TryGetValue(name, out var callback))
                {
                    int?result = null;
                    try
                    {
                        result = callback.Invoke(data.Data, name.Length + 1);

                        CoreLog.LogVerbose("Public call response for {0}: {1}", name, result);
                    }
                    catch (Exception e)
                    {
                        OnUnhandledException(new UnhandledExceptionEventArgs(e));
                    }

                    Send(ServerCommand.Response,
                         result != null
                                ? AOne.Concat(ValueConverter.GetBytes(result.Value))
                                : AZero);
                }
                else
                {
                    CoreLog.Log(CoreLogLevel.Error, "Received unknown callback " + name);
                    CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                }

                if (isFirstInit)
                {
                    if (_startBehaviour == GameModeStartBehaviour.FakeGmx)
                    {
                        _callbacks.TryGetValue("OnIncomingConnection", out var onIncomingConnection);
                        _callbacks.TryGetValue("OnPlayerConnect", out var onPlayerConnect);
                        _callbacks.TryGetValue("OnPlayerRequestClass", out var onPlayerRequestClass);

                        var natIsPlayerConnected = NativeLoader.Load("IsPlayerConnected", new[]
                        {
                            NativeParameterInfo.ForType(typeof(int))
                        });
                        var natGetPlayerPoolSize   = NativeLoader.Load("GetPlayerPoolSize", new NativeParameterInfo[0]);
                        var natForceClassSelection =
                            NativeLoader.Load("ForceClassSelection", new[]
                        {
                            NativeParameterInfo.ForType(typeof(int))
                        });
                        var natTogglePlayerSpectating = NativeLoader.Load("TogglePlayerSpectating",
                                                                          new[]
                        {
                            NativeParameterInfo.ForType(typeof(int)),
                            NativeParameterInfo.ForType(typeof(int))
                        });
                        var natGetPlayerIp = NativeLoader.Load("GetPlayerIp",
                                                               new[]
                        {
                            NativeParameterInfo.ForType(typeof(int)),
                            new NativeParameterInfo(NativeParameterType.StringReference, 2),
                            NativeParameterInfo.ForType(typeof(int)),
                        });

                        var poolSize = natGetPlayerPoolSize.Invoke();
                        for (var i = 0; i <= poolSize; i++)
                        {
                            var isConnected = natIsPlayerConnected.Invoke(i);

                            if (isConnected == 0)
                            {
                                continue;
                            }

                            var args = new object[] { i, null, 16 };
                            natGetPlayerIp.Invoke(args);

                            if (args[1] is string ip)
                            {
                                onIncomingConnection?.Invoke(
                                    ValueConverter.GetBytes(i)
                                    .Concat(ValueConverter.GetBytes(ip, Encoding.ASCII))
                                    .Concat(ValueConverter.GetBytes(9999999))
                                    .ToArray(), 0);
                            }

                            onPlayerConnect?.Invoke(ValueConverter.GetBytes(i), 0);

                            natForceClassSelection.Invoke(i);
                            natTogglePlayerSpectating.Invoke(i, 1);
                            natTogglePlayerSpectating.Invoke(i, 0);

                            onPlayerRequestClass?.Invoke(
                                ValueConverter.GetBytes(i)
                                .Concat(ValueConverter.GetBytes(0))
                                .ToArray(), 0);
                        }
                    }

                    _canTick = true;
                }
                else if (_initReceived && name == "OnGameModeExit")
                {
                    CoreLog.Log(CoreLogLevel.Info, "OnGameModeExit received, sending reconnect signal...");
                    Send(ServerCommand.Reconnect, null);

                    // Give the server time to receive the reconnect signal.
                    // TODO: This is an ugly freeze/comms-deadlock fix.
                    Thread.Sleep(100);

                    CleanUp();
                }
                break;

            default:
                CoreLog.Log(CoreLogLevel.Error, $"Unknown command {data.Command} recieved with {data.Data?.Length.ToString() ?? "NULL"} data");
                CoreLog.Log(CoreLogLevel.Debug, Environment.StackTrace);
                break;
            }
        }