{"id":13525285,"url":"https://github.com/kyleconroy/lua-state-machine","last_synced_at":"2025-04-05T10:10:10.501Z","repository":{"id":5539513,"uuid":"6743041","full_name":"kyleconroy/lua-state-machine","owner":"kyleconroy","description":"A finite state machine lua micro framework","archived":false,"fork":false,"pushed_at":"2024-04-04T14:31:22.000Z","size":35,"stargazers_count":352,"open_issues_count":4,"forks_count":66,"subscribers_count":25,"default_branch":"master","last_synced_at":"2024-10-15T20:14:54.405Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kyleconroy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2012-11-18T03:19:08.000Z","updated_at":"2024-10-12T04:09:46.000Z","dependencies_parsed_at":"2024-04-12T00:41:18.936Z","dependency_job_id":"87a7d0ac-1d5a-4756-8a73-c2e609db5f01","html_url":"https://github.com/kyleconroy/lua-state-machine","commit_stats":{"total_commits":44,"total_committers":10,"mean_commits":4.4,"dds":0.75,"last_synced_commit":"98c2ff4d147fbee1447ae8bd17b940a9fb09abec"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleconroy%2Flua-state-machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleconroy%2Flua-state-machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleconroy%2Flua-state-machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyleconroy%2Flua-state-machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kyleconroy","download_url":"https://codeload.github.com/kyleconroy/lua-state-machine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318745,"owners_count":20919484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:17.396Z","updated_at":"2025-04-05T10:10:10.483Z","avatar_url":"https://github.com/kyleconroy.png","language":"Lua","funding_links":[],"categories":["Libraries","Helpers","Lua"],"sub_categories":["Programming Language"],"readme":"Lua Finite State Machine\n========================\n\nThis standalone lua module provides a finite state machine for your pleasure.\nBased **heavily** on Jake Gordon's\n[javascript-state-machine](https://github.com/jakesgordon/javascript-state-machine).\n\nDownload\n========\n\nYou can download [statemachine.lua](https://github.com/kyleconroy/lua-state-machine/raw/master/statemachine.lua).\n\nAlternatively:\n\n    git clone git@github.com:kyleconroy/lua-state-machine\n\n\nUsage\n=====\n\nIn its simplest form, create a standalone state machine using:\n\n```lua\nlocal machine = require('statemachine')\n\nlocal fsm = machine.create({\n  initial = 'green',\n  events = {\n    { name = 'warn',  from = 'green',  to = 'yellow' },\n    { name = 'panic', from = 'yellow', to = 'red'    },\n    { name = 'calm',  from = 'red',    to = 'yellow' },\n    { name = 'clear', from = 'yellow', to = 'green'  }\n}})\n```\n\n... will create an object with a method for each event:\n\n * fsm:warn()  - transition from 'green' to 'yellow'\n * fsm:panic() - transition from 'yellow' to 'red'\n * fsm:calm()  - transition from 'red' to 'yellow'\n * fsm:clear() - transition from 'yellow' to 'green'\n\nalong with the following members:\n\n * fsm.current   - contains the current state\n * fsm.currentTransitioningEvent - contains the current event that is in a transition.\n * fsm:is(s)     - return true if state `s` is the current state\n * fsm:can(e)    - return true if event `e` can be fired in the current state\n * fsm:cannot(e) - return true if event `e` cannot be fired in the current state\n\nMultiple 'from' and 'to' states for a single event\n==================================================\n\nIf an event is allowed **from** multiple states, and always transitions to the same\nstate, then simply provide an array of states in the `from` attribute of an event. However,\nif an event is allowed from multiple states, but should transition **to** a different\nstate depending on the current state, then provide multiple event entries with\nthe same name:\n\n```lua\nlocal machine = require('statemachine')\n\nlocal fsm = machine.create({\n  initial = 'hungry',\n  events = {\n    { name = 'eat',  from = 'hungry',                                to = 'satisfied' },\n    { name = 'eat',  from = 'satisfied',                             to = 'full'      },\n    { name = 'eat',  from = 'full',                                  to = 'sick'      },\n    { name = 'rest', from = {'hungry', 'satisfied', 'full', 'sick'}, to = 'hungry'    },\n}})\n```\n\nThis example will create an object with 2 event methods:\n\n * fsm:eat()\n * fsm:rest()\n\nThe `rest` event will always transition to the `hungry` state, while the `eat` event\nwill transition to a state that is dependent on the current state.\n\n\u003e\u003e NOTE: The `rest` event could use a wildcard '*' for the 'from' state if it should be\nallowed from any current state.\n\n\u003e\u003e NOTE: The `rest` event in the above example can also be specified as multiple events with\nthe same name if you prefer the verbose approach.\n\nCallbacks\n=========\n\n4 callbacks are available if your state machine has methods using the following naming conventions:\n\n * onbefore**event** - fired before the event\n * onleave**state**  - fired when leaving the old state\n * onenter**state**  - fired when entering the new state\n * onafter**event**  - fired after the event\n\nYou can affect the event in 3 ways:\n\n * return `false` from an `onbeforeevent` handler to cancel the event.\n * return `false` from an `onleavestate` handler to cancel the event.\n * return `ASYNC` from an `onleavestate` or `onenterstate` handler to perform an asynchronous state transition (see next section)\n\nFor convenience, the 2 most useful callbacks can be shortened:\n\n * on**event** - convenience shorthand for onafter**event**\n * on**state** - convenience shorthand for onenter**state**\n\nIn addition, a generic `onstatechange()` callback can be used to call a single function for _all_ state changes:\n\nAll callbacks will be passed the same arguments:\n\n * **self**\n * **event** name\n * **from** state\n * **to** state\n * _(followed by any arguments you passed into the original event method)_\n\nCallbacks can be specified when the state machine is first created:\n\n```lua\nlocal machine = require('statemachine')\n\nlocal fsm = machine.create({\n  initial = 'green',\n  events = {\n    { name = 'warn',  from = 'green',  to = 'yellow' },\n    { name = 'panic', from = 'yellow', to = 'red'    },\n    { name = 'calm',  from = 'red',    to = 'yellow' },\n    { name = 'clear', from = 'yellow', to = 'green'  }\n  },\n  callbacks = {\n    onpanic =  function(self, event, from, to, msg) print('panic! ' .. msg)    end,\n    onclear =  function(self, event, from, to, msg) print('thanks to ' .. msg) end,\n    ongreen =  function(self, event, from, to)      print('green light')       end,\n    onyellow = function(self, event, from, to)      print('yellow light')      end,\n    onred =    function(self, event, from, to)      print('red light')         end,\n  }\n})\n\nfsm:warn()\nfsm:panic('killer bees')\nfsm:calm()\nfsm:clear('sedatives in the honey pots')\n...\n```\n\nAdditionally, they can be added and removed from the state machine at any time:\n\n```lua\nfsm.ongreen       = nil\nfsm.onyellow      = nil\nfsm.onred         = nil\nfsm.onstatechange = function(self, event, from, to) print(to) end\n```\n\nor\n```lua\nfunction fsm:onstatechange(event, from, to) print(to) end\n```\n\nAsynchronous State Transitions\n==============================\n\nSometimes, you need to execute some asynchronous code during a state transition and ensure the\nnew state is not entered until your code has completed.\n\nA good example of this is when you transition out of a `menu` state, perhaps you want to gradually\nfade the menu away, or slide it off the screen and don't want to transition to your `game` state\nuntil after that animation has been performed.\n\nYou can now return `ASYNC` from your `onleavestate` and/or `onenterstate` handlers and the state machine\nwill be _'put on hold'_ until you are ready to trigger the transition using the new `transition(eventName)`\nmethod.\n\nIf another event is triggered during a state machine transition, the event will be triggered relative to the\nstate the machine was transitioning to or from. Any calls to `transition` with the cancelled async event name\nwill be invalidated.\n\nDuring a state change, `asyncState` will transition from `NONE` to `[event]WaitingOnLeave` to `[event]WaitingOnEnter`,\nlooping back to `NONE`. If the state machine is put on hold, `asyncState` will pause depending on which handler\nyou returned `ASYNC` from.\n\nExample of asynchronous transitions:\n\n```lua\nlocal machine = require('statemachine')\nlocal manager = require('SceneManager')\n\nlocal fsm = machine.create({\n\n  initial = 'menu',\n\n  events = {\n    { name = 'play', from = 'menu', to = 'game' },\n    { name = 'quit', from = 'game', to = 'menu' }\n  },\n\n  callbacks = {\n\n    onentermenu = function() manager.switch('menu') end,\n    onentergame = function() manager.switch('game') end,\n\n    onleavemenu = function(fsm, name, from, to)\n      manager.fade('fast', function()\n        fsm:transition(name)\n      end)\n      return fsm.ASYNC -- tell machine to defer next state until we call transition (in fadeOut callback above)\n    end,\n\n    onleavegame = function(fsm, name, from, to)\n      manager.slide('slow', function()\n        fsm:transition(name)\n      end)\n      return fsm.ASYNC -- tell machine to defer next state until we call transition (in slideDown callback above)\n    end,\n  }\n})\n```\n\nIf you decide to cancel the async event, you can call `fsm.cancelTransition(eventName)`\n\nInitialization Options\n======================\n\nHow the state machine should initialize can depend on your application requirements, so\nthe library provides a number of simple options.\n\nBy default, if you dont specify any initial state, the state machine will be in the `'none'`\nstate and you would need to provide an event to take it out of this state:\n\n```lua\nlocal machine = require('statemachine')\n\nlocal fsm = machine.create({\n  events = {\n    { name = 'startup', from = 'none',  to = 'green' },\n    { name = 'panic',   from = 'green', to = 'red'   },\n    { name = 'calm',    from = 'red',   to = 'green' },\n}})\n\nprint(fsm.current) -- \"none\"\nfsm:startup()\nprint(fsm.current) -- \"green\"\n```\n\nIf you specify the name of your initial event (as in all the earlier examples), then an\nimplicit `startup` event will be created for you and fired when the state machine is constructed.\n\n```lua\nlocal machine = require('statemachine')\n\nlocal fsm = machine.create({\n  inital = 'green',\n  events = {\n    { name = 'panic',   from = 'green', to = 'red'   },\n    { name = 'calm',    from = 'red',   to = 'green' },\n}})\nprint(fsm.current) -- \"green\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyleconroy%2Flua-state-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyleconroy%2Flua-state-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyleconroy%2Flua-state-machine/lists"}