示例#1
0
文件: Program.cs 项目: Horb/stateless
        static void Main(string[] args)
        {
            try
            {
                string on = "On", off = "Off";
                var space = ' ';

                var onOffSwitch = new StateMachine<string, char>(off);

                onOffSwitch.Configure(off).Permit(space, on);
                onOffSwitch.Configure(on).Permit(space, off);

                Console.WriteLine("Press <space> to toggle the switch. Any other key will raise an error.");

                while (true)
                {
                    Console.WriteLine("Switch is in state: " + onOffSwitch.State);
                    var pressed = Console.ReadKey(true).KeyChar;
                    onOffSwitch.Fire(pressed);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey(true);
            }
        }
示例#2
0
        static void Main()
        {
            var sm = new StateMachine<State, Trigger>(State.A);

            sm.Configure(State.A)
              .Permit(Trigger.GoToB, State.B)
              .OnEntry(OnEnterA); // This never gets called.

            sm.Configure(State.B)
              .OnEntry(OnEnterB)
              .Permit(Trigger.GoToC, State.C);

            var goToCTrigger = sm.SetTriggerParameters<string>(Trigger.GoToC);

            sm.Configure(State.C)
              .OnEntryFrom(goToCTrigger, OnEnterC);

            Console.WriteLine(sm);
            sm.Fire(Trigger.GoToB);
            Console.WriteLine(sm);
            sm.Fire(goToCTrigger, "foo"); // Using Trigger.GoToC will throw an exception.
            Console.WriteLine(sm);
        }
示例#3
0
        public Story(EntityWorld world)
        {
            Acts = new List<IStoryAct>(new[] { new ActOne() });
            State = new StateMachine<StoryUpdateFunction, string>(Acts[0].Master);
            State.OnTransitioned(OnTransition);

            for (int i = 0; i < Acts.Count; i++)
            {
                var act = Acts[i];
                act.ConfigureStates(State);

                State.Configure(act.Master)
                    .Permit(Triggers.NextAct, i < Acts.Count - 1 ? (StoryUpdateFunction)Acts[i + 1].Master : EndOfStory);
            }
            World = world;
            State.Fire(ActOne.Triggers.Start);
        }
示例#4
0
 static void Fire(StateMachine<State, Trigger> phoneCall, Trigger trigger)
 {
     Console.WriteLine("[Firing:] {0}", trigger);
     phoneCall.Fire(trigger);
 }
示例#5
0
 static void SetVolume(StateMachine<State, Trigger> phoneCall, int volume)
 {
     phoneCall.Fire(setVolumeTrigger, volume);
 }
示例#6
0
        /// <summary>
        ///     Main client thread.
        /// </summary>
        private void ClientThread()
        {
            _gameState.Reset();

            _connection = DotaGameConnection.CreateWith(_details);
            _handshake = new DotaHandshake(_details, _gameState, _connection);
            _signon = new DotaSignon(_gameState, _connection, _details);
            _game = new DotaGame(_gameState, _connection);
            _commandGenerator = new UserCmdGenerator(_gameState, _connection);

            foreach (var cont in Controllers)
                cont.Initialize(_details.SteamId, _gameState, _commandGenerator);

            long handshake_requested = 0;
            long handshake_giveup = new TimeSpan(0, 0, 0, 10).Ticks;

            // Map states in the StateMachine to handlers
            var metastates = new Dictionary<States, Metastates>() {
                { States.HANDSHAKE_REQUEST, Metastates.HANDSHAKE },
                { States.HANDSHAKE_CONNECT, Metastates.HANDSHAKE },
                { States.CONNECTED, Metastates.SIGNON },
                { States.LOADING, Metastates.SIGNON },
                { States.PRESPAWN, Metastates.SIGNON },
                { States.SPAWN, Metastates.SIGNON },
                { States.PLAY, Metastates.GAME },
            };

            var processors = new Dictionary<Metastates, IHandler>() {
                { Metastates.HANDSHAKE, _handshake },
                { Metastates.SIGNON, _signon },
                { Metastates.GAME, _game }
            };


            _stateMachine = new StateMachine<States, Events>(States.DISCONNECTED);
            //temporary shit
            _stateMachine.OnTransitioned(transition =>
            {
                if (transition.Source == transition.Destination) return;
                Callback?.Invoke(this, new CallbackEventArgs(new DotaGameClient.SessionStateTransition(transition.Source, transition.Destination)));
            });

            _stateMachine.OnUnhandledTrigger((states, events) =>
            {
                Console.WriteLine("Unhandled trigger: " + events.ToString("G"));

            });

            var disconnected = new Action(() =>
            {
                if (_connection == null) return;
                Running = true;
                Stop();
            });

            _stateMachine.Configure(States.DISCONNECTED)
                .Ignore(Events.TICK)
                .Ignore(Events.DISCONNECTED)
                .OnEntry(disconnected)
                .Permit(Events.REQUEST_CONNECT, States.HANDSHAKE_REQUEST);
            _stateMachine.Configure(States.HANDSHAKE_REJECTED)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED)
                .OnEntry(() =>
                {
                    Callback?.Invoke(this, new CallbackEventArgs(new DotaGameClient.HandshakeRejected(_handshake.rejected_reason)));
                    _stateMachine.Fire(Events.DISCONNECTED);
                });
            _stateMachine.Configure(States.HANDSHAKE_REQUEST)
                .OnEntry(() => handshake_requested = DateTime.Now.Ticks)
                .OnEntry(_handshake.RequestHandshake)
                .Ignore(Events.TICK)
                .Permit(Events.HANDSHAKE_CHALLENGE, States.HANDSHAKE_CONNECT)
                .Permit(Events.REJECTED, States.HANDSHAKE_REJECTED)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);
            _stateMachine.Configure(States.HANDSHAKE_CONNECT)
                .OnEntry(_handshake.RespondHandshake)
                .Ignore(Events.TICK)
                .PermitReentry(Events.HANDSHAKE_CHALLENGE) // possibly re-enter?
                .Permit(Events.HANDSHAKE_COMPLETE, States.CONNECTED)
                .Permit(Events.REJECTED, States.HANDSHAKE_REJECTED)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);
            _stateMachine.Configure(States.CONNECTED)
                .OnEntry(_signon.EnterConnected)
                .Ignore(Events.TICK)
                .Permit(Events.LOADING_START, States.LOADING)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);
            _stateMachine.Configure(States.LOADING)
                .OnEntry(_signon.EnterNew)
                .Ignore(Events.TICK)
                .Permit(Events.CONNECTED, States.CONNECTED)
                .Permit(Events.PRESPAWN_START, States.PRESPAWN)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);
            _stateMachine.Configure(States.PRESPAWN)
                .OnEntry(_signon.EnterPrespawn)
                .Ignore(Events.TICK)
                .Permit(Events.SPAWNED, States.SPAWN)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);
            _stateMachine.Configure(States.SPAWN)
                .OnEntry(_signon.EnterSpawn)
                .Ignore(Events.TICK)
                .Permit(Events.BASELINE, States.PLAY)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);
            _stateMachine.Configure(States.PLAY)
                .OnEntryFrom(Events.BASELINE, () =>
                {
                    _game.EnterGame();
                    _commandGenerator.Reset();
                })
                .OnEntryFrom(Events.TICK, () =>
                {
                    _gameState.Update();
                    foreach(var cont in Controllers) cont.Tick();
                    _commandGenerator.Tick();
                    _gameState.Created.Clear();
                    _gameState.Deleted.Clear();
                })
                .PermitReentry(Events.TICK)
                .Permit(Events.DISCONNECTED, States.DISCONNECTED);

            _stateMachine.Fire(Events.REQUEST_CONNECT);

            long next_tick = DateTime.Now.Ticks;
            while (Running && _stateMachine.State != States.DISCONNECTED && _stateMachine.State != States.HANDSHAKE_REJECTED)
            {
                try
                {
                    if (next_tick > DateTime.Now.Ticks)
                    {
                        Thread.Sleep(1);
                        continue;
                    }

                    if (_stateMachine == null) break;

                    if (_stateMachine.State == States.HANDSHAKE_REQUEST &&
                        (DateTime.Now.Ticks - handshake_requested) > handshake_giveup)
                    {
                        _stateMachine.Fire(Events.DISCONNECTED);
                        continue;
                    }

                    if (_connection.state == DotaGameConnection.State.Closed)
                    {
                        _stateMachine.Fire(Events.DISCONNECTED);
                        continue;
                    }

                    if (_connection == null) break;

                    List<byte[]> outBand = _connection.GetOutOfBand();
                    List<DotaGameConnection.Message> inBand = _connection.GetInBand();

                    foreach (byte[] message in outBand)
                    {
                        Nullable<Events> e = processors[metastates[_stateMachine.State]].Handle(message);
                        if (e.HasValue)
                        {
                            _stateMachine.Fire(e.Value);
                        }
                    }

                    foreach (DotaGameConnection.Message message in inBand)
                    {
                        Nullable<Events> e = processors[metastates[_stateMachine.State]].Handle(message);
                        if (e.HasValue)
                        {
                            _stateMachine.Fire(e.Value);
                        }
                    }

                    _stateMachine.Fire(Events.TICK);

                    if (_gameState.TickInterval > 0)
                    {
                        next_tick += (uint)(_gameState.TickInterval * 1000 * 10000 /* ticks per ms */);
                    }
                    else
                    {
                        next_tick += 50 * 1000;
                    }
                    int remain = (int)(next_tick - DateTime.Now.Ticks) / 10000;
                    if (remain > 0)
                    {
                        Thread.Sleep(1);
                    }
                    else if (remain < 0)
                    {
                        next_tick = DateTime.Now.Ticks;
                    }
                }
                catch (Exception ex)
                {
                    Callback?.Invoke(this, new CallbackEventArgs(new DotaGameClient.LogMessage("Ignored error in session loop, " + ex.Message)));
                }
            }

            if (Running) Stop();
        }
        private void ConfigureMachine()
        {
            m_GameMachine = new StateMachine<State, Trigger>(() => m_CurrentState, newState => m_CurrentState = newState);
            ProcessMessages();

            #region WaitingForPlayers
            m_GameMachine.Configure(State.WaitingForPlayers)
                .Permit(Trigger.AllPlayersReady, State.AllPlayersReady)
                .OnEntryFrom(Trigger.PlayerNotReady, x =>
                {
                    GenericClientMessage waiting = new GenericClientMessage() { MessageEnum = ClientMessage.ClientMessageEnum.PlayerNotReady };
                    LookupPlayerById(m_GameCreatorClientId).Callback.SendMessage(waiting.ToXml());
                })
                .OnEntryFrom(Trigger.PlayerLeft, x =>
                {
                    SendUpdatedGameInfo();
                })
                .PermitReentry(Trigger.PlayerLeft)
                .Ignore(Trigger.PlayerNotReady)
                .Ignore(Trigger.CreatorPressedStart);
            #endregion

            #region AllPlayersReady
            m_GameMachine.Configure(State.AllPlayersReady)
                .OnEntry(x =>
                {
                    GenericClientMessage ready = new GenericClientMessage() { MessageEnum = ClientMessage.ClientMessageEnum.AllPlayersReady };
                    LookupPlayerById(m_GameCreatorClientId).Callback.SendMessage(ready.ToXml());
                })
                .Permit(Trigger.PlayerNotReady, State.WaitingForPlayers)
                .Permit(Trigger.PlayerLeft, State.WaitingForPlayers)
                .Permit(Trigger.CreatorPressedStart, State.GameStarted);
            #endregion

            #region GameStarted
            m_GameMachine.Configure(State.GameStarted)
                .OnEntry(x =>
                {
                    List<KeyValuePair<string, Army.ArmyTypeEnum>> PlayerInfos = new List<KeyValuePair<string, Army.ArmyTypeEnum>>();
                    foreach (Player player in Players)
                    {
                        PlayerInfos.Add(new KeyValuePair<string, Army.ArmyTypeEnum>(player.ClientId, player.ArmyType));
                        m_PlayerTurnOrder.Enqueue(player.ClientId);
                    }
                    GameState = new GameState(PlayerInfos);
                    pathFinder = new PathFinder(GameState);
                    ConsoleLogger.Push(String.Format("Game {0} has been started", Name));
                    NotifyAllPlayers(new GenericClientMessage() { MessageEnum = ClientMessage.ClientMessageEnum.TransitionToLoadingState }.ToXml()); // switch all the players to loading while we send the gamestate
                    ClientGameStateMessage gamestate = new ClientGameStateMessage() { State = GameState };
                    GenericClientMessage start = new GenericClientMessage() { MessageEnum = ClientMessage.ClientMessageEnum.StartGame };
                    AggregateMessage aggregate = new AggregateMessage();
                    aggregate.MessageList.Add(gamestate);
                    aggregate.MessageList.Add(start);
                    NotifyAllPlayers(aggregate.ToXml());
                    m_GameMachine.Fire(Trigger.GameStarted);
                })
                .Permit(Trigger.GameStarted, State.PlayerTurn)
                .Ignore(Trigger.PlayerNotReady);
            #endregion

            #region PlayerTurn
            m_GameMachine.Configure(State.PlayerTurn)
                .PermitReentry(Trigger.PlayerTurn)
                .Permit(Trigger.PlayerLeft, State.Reconfigure)
                .Permit(Trigger.StartBattlePhase, State.BattlePhase)
                .Permit(Trigger.Cheatcode, State.EndGame)
                .OnEntry(x =>
                {
                    // notify the current player that its their turn
                    m_CurrentPlayerId = m_PlayerTurnOrder.Dequeue();
                    LookupPlayerById(m_CurrentPlayerId).Callback.SendMessage((new GenericClientMessage() { MessageEnum = ClientMessage.ClientMessageEnum.ActiveTurn }).ToXml());

                    // notify all other players that they have to wait
                    foreach (string playerId in m_PlayerTurnOrder)
                    {
                        LookupPlayerById(playerId).Callback.SendMessage((new WaitingForTurnMessage() { ActivePlayerName = LookupPlayerById(m_CurrentPlayerId).Name }).ToXml());
                    }

                    // add the current player back on the queue
                    m_PlayerTurnOrder.Enqueue(m_CurrentPlayerId);
                });
            #endregion

            m_GameMachine.Configure(State.BattlePhase)
                .OnEntry(x =>
                {

                });

            #region EndGame
            m_GameMachine.Configure(State.EndGame)
                .OnEntry(x =>
                {
                    var winner = LookupPlayerById(m_WinnerId);
                    var gameOverMessage = new GameOverMessage() { WinnerId = m_WinnerId, WinnerName = winner.Name };
                    NotifyAllPlayers(gameOverMessage.ToXml());
                });
            #endregion

            #region Reconfigure
            m_GameMachine.Configure(State.Reconfigure)
                .OnEntry(x =>
                {
                    // this state allows us to reconfigure the players involved in the game in case someone leaves or is defeated
                    Queue<string> tempPlayers = new Queue<string>();
                    foreach (string playerId in m_PlayerTurnOrder)
                    {
                        if (Players.FirstOrDefault(p => p.ClientId == playerId) != null)
                        {
                            tempPlayers.Enqueue(playerId);
                        }
                    }
                    if (tempPlayers.Count > 0)
                    {
                        m_PlayerTurnOrder.Clear();
                        m_PlayerTurnOrder = tempPlayers;
                        m_GameMachine.Fire(Trigger.PlayerTurn);
                    }
                })
                .Permit(Trigger.PlayerTurn, State.PlayerTurn);
            #endregion
        }