Esempio n. 1
0
        /// <summary>
        ///     Invokes the native with the specified arguments.
        /// </summary>
        /// <param name="arguments">The arguments.</param>
        /// <returns>The return value of the native.</returns>
        public int Invoke(params object[] arguments)
        {
            if (arguments == null)
            {
                throw new ArgumentNullException(nameof(arguments));
            }

            if (Parameters.Length != arguments.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(arguments), "Invalid argument count");
            }

            IEnumerable <byte> data = ValueConverter.GetBytes(Handle);

            if (CoreLog.DoesLog(CoreLogLevel.Verbose))
            {
                CoreLog.LogVerbose("Invoking {0}({1})", Name, string.Join(", ", arguments));
            }

            int length;

            for (var i = 0; i < Parameters.Length; i++)
            {
                length = GetLength(i, arguments);

                data = data
                       .Concat(new[] { (byte)Parameters[i].ArgumentType })
                       .Concat(Parameters[i].GetBytes(arguments[i], length, _gameModeClient));
            }

            var response = _gameModeClient.InvokeNative(data);

            if (response.Length < 4)
            {
                CoreLog.Log(CoreLogLevel.Warning, "Native call returned no response, execution probably failed.");
                return(0);
            }

            var result  = ValueConverter.ToInt32(response, 0);
            var respPos = 4;

            for (var i = 0; i < Parameters.Length; i++)
            {
                length = GetLength(i, arguments);

                var value = Parameters[i].GetReferenceArgument(response, ref respPos, length, result, _gameModeClient);
                if (value != null)
                {
                    arguments[i] = value;
                }
            }

            return(result);
        }
        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;
            }
        }
Esempio n. 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;
            }
        }