mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
[api] add server mode
This commit is contained in:
parent
77c6ac6ba2
commit
49c9741349
24 changed files with 1553 additions and 52 deletions
32
doc/api.md
32
doc/api.md
|
|
@ -21,20 +21,20 @@ run_state:run_file("script.ans")
|
|||
|
||||
-- run the script
|
||||
while run_state:active() do
|
||||
local e, data = run_state:step()
|
||||
if e == "text" then
|
||||
local event, data = run_state:step()
|
||||
if event == "text" then
|
||||
for _, l in ipairs(data) do
|
||||
print(l)
|
||||
end
|
||||
elseif e == "choice" then
|
||||
elseif event == "choice" then
|
||||
for i, l in ipairs(data) do
|
||||
print(("%s> %s"):format(i, l))
|
||||
end
|
||||
local choice = tonumber(io.read("l"))
|
||||
data:choose(choice)
|
||||
elseif e == "return" then
|
||||
elseif event == "return" then
|
||||
run_state:merge()
|
||||
elseif e == "error" then
|
||||
elseif event == "error" then
|
||||
error(data)
|
||||
end
|
||||
end
|
||||
|
|
@ -146,7 +146,7 @@ _defined at line 76 of [anselme/state/State.lua](../anselme/state/State.lua):_ `
|
|||
|
||||
### .source_branch
|
||||
|
||||
State this State was branched from.
|
||||
State this State was branched from. `nil` if this is the main branch.
|
||||
|
||||
_defined at line 78 of [anselme/state/State.lua](../anselme/state/State.lua):_ `source_branch = nil,`
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ _defined at line 148 of [anselme/state/State.lua](../anselme/state/State.lua):_
|
|||
|
||||
### :active ()
|
||||
|
||||
Indicate if a script is currently loaded in this branch.
|
||||
Returns true if a script is currently loaded in this branch, false otherwise.
|
||||
|
||||
_defined at line 163 of [anselme/state/State.lua](../anselme/state/State.lua):_ `active = function(self)`
|
||||
|
||||
|
|
@ -376,13 +376,13 @@ else
|
|||
end
|
||||
```
|
||||
|
||||
_defined at line 74 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `local TextEventData`
|
||||
_defined at line 87 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `local TextEventData`
|
||||
|
||||
### :group_by (tag_name)
|
||||
### :group_by (tag_key)
|
||||
|
||||
Returns a list of TextEventData where the first part of each LuaText of each TextEventData has the same value for the tag `tag_name`.
|
||||
Returns a list of TextEventData where the first part of each LuaText of each TextEventData has the same value for the tag `tag_key`.
|
||||
|
||||
In other words, this groups all the LuaTexts contained in this TextEventData using the `tag_name` tag and returns a list containing these groups.
|
||||
In other words, this groups all the LuaTexts contained in this TextEventData using the `tag_key` tag and returns a list containing these groups.
|
||||
|
||||
For example, with the following Anselme script:
|
||||
```
|
||||
|
|
@ -399,7 +399,7 @@ calling `text_event_data:group_by("speaker")` will return a list of three TextEv
|
|||
* the second with the text "C"; with the tag `speaker="Lana"`
|
||||
* the last with the text "D"; wiith the tag `speaker="John"`
|
||||
|
||||
_defined at line 96 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `group_by = function(self, tag_name)`
|
||||
_defined at line 109 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `group_by = function(self, tag_key)`
|
||||
|
||||
|
||||
## ChoiceEventData
|
||||
|
|
@ -467,13 +467,13 @@ A text will typically only consist of a single part unless it was built using te
|
|||
|
||||
Each text part is a table containing `text` (string) and `tags` (table) properties, for example: `{ text = "text part string", tags = { color = "red" } }`.
|
||||
|
||||
_defined at line 17 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `local LuaText`
|
||||
_defined at line 19 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `local LuaText`
|
||||
|
||||
### .raw
|
||||
|
||||
Anselme Text value this was created from. For advanced usage only. See the source file [Text.lua](anselme/ast/Text.lua) for more information.
|
||||
|
||||
_defined at line 25 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `raw = nil,`
|
||||
_defined at line 27 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `raw = nil,`
|
||||
|
||||
### :__tostring ()
|
||||
|
||||
|
|
@ -481,7 +481,7 @@ Returns a text representation of the LuaText, using Anselme's default formatting
|
|||
|
||||
Usage: `print(luatext)`
|
||||
|
||||
_defined at line 39 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `__tostring = function(self)`
|
||||
_defined at line 41 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `__tostring = function(self)`
|
||||
|
||||
---
|
||||
_file generated at 2024-11-11T13:33:43Z_
|
||||
_file generated at 2024-11-17T15:00:50Z_
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
-- Behold! A documentation generator that doesn't try to be smart!
|
||||
-- Call this from the root anselme repository directory: `lua doc/gendocs.lua`
|
||||
|
||||
local utf8 = utf8 or require("lua-utf8")
|
||||
local utf8 = utf8 or (love and require("utf8") or require("lua-utf8"))
|
||||
|
||||
local files = {
|
||||
"doc/api.md",
|
||||
"doc/server.md",
|
||||
"doc/standard_library.md"
|
||||
}
|
||||
local source_link_prefix = "../"
|
||||
|
|
|
|||
336
doc/server.md
Normal file
336
doc/server.md
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
Instead of the scripts running in the same Lua process as the one of your game, Anselme can run in a Client-Server mode. This allows:
|
||||
|
||||
* Anselme to run in a separate thread and therefore not affect your game's frame times (Anselme is not very fast)
|
||||
* to use Anselme other game engine that don't use Lua
|
||||
|
||||
The _server_ is the process that holds and process the Anselme state. Typically, the server would run in a separate process or thread that your game.
|
||||
|
||||
The _client_ connects to a server and sends instructions to execute Anselmes scripts and receive the response. Typically, the client correspond to your game.
|
||||
|
||||
For now, the whole system assumes that there is a single client per server - so you should not share a single server among serveral client.
|
||||
|
||||
How the Client and Server communicate between each other is defined using a RPC object.
|
||||
Out-of-the-box, Anselme provides RPC objects that can communicate over [LÖVE](https://www.love2d.org/) threads, and over [JSON-RPC 2.0](https://www.jsonrpc.org/specification); these can be easily created using the functions in [anselme.server](#anselme_server).
|
||||
If you want to implement a custom RPC mechanism, you can look at the existing implementations in `anselme/server/rpc/`.
|
||||
|
||||
Example usage in a LÖVE game:
|
||||
```lua
|
||||
local server = require("anselme.server")
|
||||
|
||||
-- create a new client+server
|
||||
local client = server.new_love_thread()
|
||||
client:load_stdlib()
|
||||
|
||||
-- load an anselme script file in a new branch
|
||||
local run_state = client:branch("block")
|
||||
run_state:run_file("script.ans")
|
||||
|
||||
-- start script
|
||||
run_state:step(handle_event)
|
||||
|
||||
-- callback to handle an anselme event
|
||||
function handle_event(event, data)
|
||||
if event == "text" then
|
||||
show_dialog_box {
|
||||
lines = data,
|
||||
on_dialog_box_closed = function()
|
||||
run_state:step(handle_event) -- resume script
|
||||
end
|
||||
}
|
||||
elseif event == "choice" then
|
||||
show_choice_dialog_box {
|
||||
choices = data,
|
||||
on_dialog_box_closed = function(choice_number)
|
||||
run_state:choose(choice_number)
|
||||
run_state:step(handle_event)
|
||||
end
|
||||
}
|
||||
elseif event == "return" then
|
||||
run_state:merge()
|
||||
run_state:remove() -- remove branch from server
|
||||
elseif event == "error" then
|
||||
print("error in anselme thread!", data)
|
||||
run_state:remove()
|
||||
end
|
||||
end
|
||||
|
||||
function love.update()
|
||||
client:process() -- handle messages coming from the server
|
||||
end
|
||||
```
|
||||
|
||||
# anselme.server
|
||||
|
||||
Main functions to create clients and servers.
|
||||
|
||||
### .new_love_thread ()
|
||||
|
||||
Starts a Server in a new LÖVE thread and returns a Client connected to that server.
|
||||
|
||||
Should be called from a [LÖVE](https://www.love2d.org/) game code only.
|
||||
|
||||
_defined at line 10 of [anselme/server/init.lua](../anselme/server/init.lua):_ `new_love_thread = function()`
|
||||
|
||||
### .new_json_rpc_server (send, receive)
|
||||
|
||||
Returns a new Server that communicate with a Client using JSON-RPC 2.0.
|
||||
|
||||
This does not define _how_ the two comminicate (through sockets, http, etc.), you will need to define this using the `send` and `receive` arguments.
|
||||
|
||||
`send(message)` is a function that send a single message to the associated Client.
|
||||
|
||||
`receive(block)` is a function that receive a single message from the associated Client (or `nil` if no message available). If `block` is true, the function is allowed to block execution until a message is received.
|
||||
|
||||
_defined at line 41 of [anselme/server/init.lua](../anselme/server/init.lua):_ `new_json_rpc_server = function(send, receive)`
|
||||
|
||||
### .new_json_rpc_client (send, receive)
|
||||
|
||||
Returns a new Client that communicate with a Server using JSON-RPC 2.0.
|
||||
|
||||
This does not define _how_ the two comminicate (through sockets, http, etc.), you will need to define this using the `send` and `receive` arguments.
|
||||
|
||||
`send(message)` is a function that send a single message to the associated Server.
|
||||
|
||||
`receive(block)` is a function that receive a single message from the associated Server (or `nil` if no message available). If `block` is true, the function is allowed to block execution until a message is received.
|
||||
|
||||
_defined at line 53 of [anselme/server/init.lua](../anselme/server/init.lua):_ `new_json_rpc_client = function(send, receive)`
|
||||
|
||||
|
||||
# Client
|
||||
|
||||
This is a Lua implementation of an Anselme client, with a nice API that mirrors the Anselme [State API](api.md#state) to communicate with the server.
|
||||
|
||||
Usage: create a Client object using the functions in the [anselme.server module](#anselme_server) and call `server:process()` regularly to process messages from the Server.
|
||||
|
||||
The API available here tries to follow the [State API](api.md#state) as much as possible, with the following differences:
|
||||
* functions that return a value in State take an additionnal argument `callback`:
|
||||
* if it is a function `callback(ret1, ret2, ...)`, it is called as soon as the return values `ret1, ret2, ...` are received. The function also returns the identifier `call_id` associated with the callback (to optionally cancel the callback later using `client:cancel(call_id)`).
|
||||
* if it is `nil`, return values are discarded;
|
||||
* if it is the string `"block"`, the call will block until the return values are received. The function returns these values directly.
|
||||
* functions that returns a `State` in State now returns a `Client`;
|
||||
* return values are converted to a simpler representation if possible (no metamethods, userdata or cycles) to make serialization simpler - in particular, Anselme values are automatically converted to Lua primitives.
|
||||
* a few new methods are introduced, see below.
|
||||
|
||||
Implementing a Client in other languages should be relatively easy: if your client language has a [JSON-RPC 2.0](https://www.jsonrpc.org/specification) library, point it to the Anselme server you started using [`server.new_json_rpc_server()`](#new_json_rpc_server) and you're done.
|
||||
You should then be able to call any of the methods described in the [Server](#server).
|
||||
Additionnaly, if you plan to use the `define_rpc` or `define_local_rpc` server methods, you will need to implement the following remote method in your client that will be called by the server:
|
||||
* `call(function_id, ...)` where `function_id` (string) is the function identifier that was given when `define_rpc` or `define_local_rpc` was called, and `...` is a list of arguments. This must call the function associated with the `function_id` using the given arguments, and returns the values returned by the call (as a list of return values: `{ret1, ret2, ...}`).
|
||||
|
||||
### :process (block)
|
||||
|
||||
Process received messages.
|
||||
|
||||
Must be called regularly.
|
||||
If `block` is true, the function is allowed to block execution until a message is received.
|
||||
|
||||
_defined at line 51 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `process = function(self, block)`
|
||||
|
||||
### :cancel (call_id)
|
||||
|
||||
Cancel the callback associated with the call `call_id`.
|
||||
This does not stop the remote method execution; only prevent the callback from being called.
|
||||
|
||||
_defined at line 57 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `cancel = function(self, call_id)`
|
||||
|
||||
### :choose (i)
|
||||
|
||||
If the last event was a `choice`, choose the `i`-th choice.
|
||||
This must be called before calling `:step` again after receiving a choice event.
|
||||
|
||||
_defined at line 63 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `choose = function(self, i)`
|
||||
|
||||
### :remove ()
|
||||
|
||||
Remove the branch from the server.
|
||||
The branch (and therefore this Client branch) can't be used after calling this method.
|
||||
|
||||
_defined at line 68 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `remove = function(self)`
|
||||
|
||||
### :define_rpc (name, args, func)
|
||||
|
||||
Defines a function in the global scope, that calls the Lua function `func` on the Client when called.
|
||||
|
||||
The function will not be sent to the server; it will be directly executed on the client (i.e. your game code)
|
||||
each time a script on the server needs it to be called.
|
||||
|
||||
Usage: `client:define_rpc("teleport", "(position)", function(position) player:teleport(position) end)`
|
||||
|
||||
_defined at line 78 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `define_rpc = function(self, name, args, func)`
|
||||
|
||||
### :define_local_rpc (name, args, func)
|
||||
|
||||
Same as `:define_rpc`, but define the function in the current scope.
|
||||
|
||||
_defined at line 85 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `define_local_rpc = function(self, name, args, func)`
|
||||
|
||||
## Methods and fields that mirror the State API
|
||||
|
||||
### :load_stdlib (language)
|
||||
|
||||
Same as [`state:load_stdlib(language)`](api.md#load_stdlib-language).
|
||||
|
||||
_defined at line 95 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `load_stdlib = function(self, language)`
|
||||
|
||||
### .branch_id
|
||||
|
||||
Same as [`state.branch_id`](api.md#branch_id).
|
||||
|
||||
_defined at line 100 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `branch_id = "main",`
|
||||
|
||||
### .source_branch
|
||||
|
||||
Same as [`state.source_branch`](api.md#source_branch), but refers to the source `Client` instead of a `State`.
|
||||
|
||||
_defined at line 102 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `source_branch = nil,`
|
||||
|
||||
### :branch (branch_id, callback)
|
||||
|
||||
Same as [`state:branch(branch_id)`](api.md#branch-branch_id), but returns a new `Client` instead of a `State`.
|
||||
|
||||
_defined at line 104 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `branch = function(self, branch_id, callback)`
|
||||
|
||||
### :merge ()
|
||||
|
||||
Same as [`state:merge()`](api.md#merge).
|
||||
|
||||
_defined at line 113 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `merge = function(self)`
|
||||
|
||||
### :define (name, value, func_code, raw_mode)
|
||||
|
||||
Same as [`state:define(name, value, func, raw_mode)`](api.md#api.md#define-name-value-func-raw_mode), but if `func_code` is given, it must be a string containing the function code.
|
||||
|
||||
Note that the given code will be executed on the server, and that there is no sandboxing of any kind;
|
||||
|
||||
Example: `client:define("main", "print", "(message::is string)", "function(message) print(message) end")`.
|
||||
|
||||
_defined at line 122 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `define = function(self, name, value, func_code, raw_mode)`
|
||||
|
||||
### :define_local (name, value, func_code, raw_mode)
|
||||
|
||||
Same as [`define`](#define-name-value-func_code-raw_mode), but calls [`state:define_local(name, value, func, raw_mode)`](api.md#api.md#define_local-name-value-func-raw_mode).
|
||||
|
||||
_defined at line 126 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `define_local = function(self, name, value, func_code, raw_mode)`
|
||||
|
||||
### :defined (name, callback)
|
||||
|
||||
Same as [`state:defined(name)`](api.md#defined-name).
|
||||
|
||||
_defined at line 130 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `defined = function(self, name, callback)`
|
||||
|
||||
### :defined_local (name, callback)
|
||||
|
||||
Same as [`state:defined_local(name)`](api.md#defined_local-name).
|
||||
|
||||
_defined at line 134 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `defined_local = function(self, name, callback)`
|
||||
|
||||
### :save (callback)
|
||||
|
||||
Same as [`state:save()`](api.md#save).
|
||||
|
||||
_defined at line 139 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `save = function(self, callback)`
|
||||
|
||||
### :load (save)
|
||||
|
||||
Same as [`state:load(save)`](api.md#load-save).
|
||||
|
||||
_defined at line 143 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `load = function(self, save)`
|
||||
|
||||
### :active (callback)
|
||||
|
||||
Same as [`state:active()`](api.md#active).
|
||||
|
||||
_defined at line 148 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `active = function(self, callback)`
|
||||
|
||||
### :state (callback)
|
||||
|
||||
Same as [`state:state()`](api.md#state).
|
||||
|
||||
_defined at line 152 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `state = function(self, callback)`
|
||||
|
||||
### :run (code, source, tags)
|
||||
|
||||
Same as [`state:run(code, source, tags)`](api.md#run-code-source-tags).
|
||||
|
||||
_defined at line 156 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `run = function(self, code, source, tags)`
|
||||
|
||||
### :run_file (path, tags)
|
||||
|
||||
Same as [`state:run_file(code, source, tags)`](api.md#run_file-code-source-tags).
|
||||
|
||||
_defined at line 160 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `run_file = function(self, path, tags)`
|
||||
|
||||
### :step (callback)
|
||||
|
||||
Same as [`state:step)`](api.md#step), but returns:
|
||||
* for `text` and `choice` events, a list of lines `{ { { text = "line 1 part 2", tags = { ... } }, ... }, ... }` (in other word, [`TextEventData`](api.md#texteventdata) and [`ChoiceEventData`](api.md#choiceeventdata) stripped of everything but their list of text parts);
|
||||
* for `return` events, the return value converted to Lua primitives;
|
||||
* for other events, it will try to return the event data as-is.
|
||||
|
||||
_defined at line 167 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `step = function(self, callback)`
|
||||
|
||||
### :interrupt (code, source, tags)
|
||||
|
||||
Same as [`state:interrupt(code, source, tags)`](api.md#interrupt-code-source-tags).
|
||||
|
||||
_defined at line 171 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `interrupt = function(self, code, source, tags)`
|
||||
|
||||
### :eval (code, source, tags, callback)
|
||||
|
||||
Same as [`state:eval(code, source, tags)`](api.md#eval-code-source-tags), but the returned value is converted to Lua primitives.
|
||||
|
||||
_defined at line 175 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `eval = function(self, code, source, tags, callback)`
|
||||
|
||||
### :eval_local (code, source, tags, callback)
|
||||
|
||||
Same as [`state:eval_local(code, source, tags)`](api.md#eval_local-code-source-tags), but the returned value is converted to Lua primitives.
|
||||
|
||||
_defined at line 179 of [anselme/server/Client.lua](../anselme/server/Client.lua):_ `eval_local = function(self, code, source, tags, callback)`
|
||||
|
||||
|
||||
# Server
|
||||
|
||||
An Anselme server instance.
|
||||
|
||||
Usage: create a Server object using the functions in the [anselme.server module](#anselme_server) and call `server:process()` regularly to process messages from the Client.
|
||||
|
||||
If you are implementing your own client, the following methods are available to be remotely called by your client:
|
||||
* Note:
|
||||
* in all the following methods, the first parameter `branch_id` (string) is the id of the Anselme branch to operate on;
|
||||
* methods that return something always returns a list of return values: `{ ret1, ret2, ... }`.
|
||||
* `choose(branch_id, i)`: if the last event was a `choice`, choose the `i`-th (number) line in the choice list;
|
||||
* `remove(branch_id)`: removes the branch from the server; no further operation will be possible on the branch;
|
||||
* `load_stdlib(branch_id, language)`: calls [`state:load_stdlib(language)`](api.md#load_stdlib-language) on the branch;
|
||||
* `branch(branch_id[, new_branch_id])`: calls [`state:branch(branch_id)`](api.md#branch-branch_id) on the branch; returns the id of the new branch (string);
|
||||
* `merge(branch_id)`: calls [`state:merge()`](api.md#merge) on the branch;
|
||||
* `define(branch_id, name, args, func_code, raw_mode)`: calls [`state:define(branch_id, name, args, func, raw_mode)`](api.md#define-name-value-func-raw_mode) on the branch; if `func_code` is given, `func` will be a function generated from the Lua code `func_code` (string, example: `define("main", "print", "(message::is string)", "function(message) print(message) end")`). Note that whatever is in `func_code` will be executed on the server, and that there is no sandboxing of any kind;
|
||||
* `define_rpc(branch_id, name, args, func_id)`: defines a function in the branch that, when called, will call the remote method `call(func_id, ...)` on the client and block until it returns. In other words, this allows the Anselme script running on the server to transparently call the function that is associated with the id `func_id` on the client.
|
||||
* `define_local(branch_id, name, args, func_code, raw_mode)`: same as `define`, but calls [`state:define_local(branch_id, name, args, func, raw_mode)`](api.md#define_local-name-value-func-raw_mode);
|
||||
* `define_local_rpc(branch_id, name, args, func_id)`: same as `define_rpc`, but defines the function in the current scope;
|
||||
* `defined(branch_id, name)`: calls [`state:defined(name)`](api.md#defined-name) on the branch and returns its result;
|
||||
* `defined_local(branch_id, name)`: calls [`state:defined_local(name)`](api.md#defined_local-name) on the branch and returns its result;
|
||||
* `save(branch_id)`: calls [`state:save()`](api.md#save) on the branch and returns its result;
|
||||
* `load(branch_id, save)`: calls [`state:load(save)`](api.md#load-save) on the branch;
|
||||
* `active(branch_id)`: calls [`state:active()`](api.md#active) on the branch and returns its result;
|
||||
* `state(branch_id)`: calls [`state:state()`](api.md#state) on the branch and returns its result;
|
||||
* `run(branch_id, code, source, tags)`: calls [`state:run(code, source, tags)`](api.md#run-code-source-tags) on the branch;
|
||||
* `run_file(branch_id, path, tags)`: calls [`state:run_file(path, tags)`](api.md#run_file-path-tags) on the branch;
|
||||
* `step(branch_id)`: calls [`state:step()`](api.md#step) on the branch and returns:
|
||||
* for `text` and `choices` events, a list of lines `{ { { text = "line 1 part 2", tags = { ... } }, ... }, ... }` (in other word, [`TextEventData`](api.md#texteventdata) and [`ChoiceEventData`](api.md#choiceeventdata) stripped of everything but their list of text parts);
|
||||
* for `return` events, the return value converted to Lua;
|
||||
* for other events, it will try to return the event data as-is.
|
||||
* `interrupt(branch_id, code, source, tags)`: calls [`state:interrupt(code, source, tags)`](api.md#interrupt-code-source-tags) on the branch;
|
||||
* `eval(branch_id, code, source, tags)`: calls [`state:eval(code, source, tags)`](api.md#eval-code-source-tags) on the branch and returns its result, converted to Lua;
|
||||
* `eval_local(branch_id, code, source, tags)`: calls [`state:eval_local(code, source, tags)`](api.md#eval_local-code-source-tags) on the branch and returns its result, converted to Lua.
|
||||
|
||||
### :process (block)
|
||||
|
||||
Process received messages.
|
||||
|
||||
Must be called regularly.
|
||||
If `block` is true, the function is allowed to block execution until a message is received.
|
||||
|
||||
_defined at line 160 of [anselme/server/Server.lua](../anselme/server/Server.lua):_ `process = function(self, block)`
|
||||
|
||||
|
||||
---
|
||||
_file generated at 2024-11-17T15:00:50Z_
|
||||
72
doc/server.md.template
Normal file
72
doc/server.md.template
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
Instead of the scripts running in the same Lua process as the one of your game, Anselme can run in a Client-Server mode. This allows:
|
||||
|
||||
* Anselme to run in a separate thread and therefore not affect your game's frame times (Anselme is not very fast)
|
||||
* to use Anselme other game engine that don't use Lua
|
||||
|
||||
The _server_ is the process that holds and process the Anselme state. Typically, the server would run in a separate process or thread that your game.
|
||||
|
||||
The _client_ connects to a server and sends instructions to execute Anselmes scripts and receive the response. Typically, the client correspond to your game.
|
||||
|
||||
For now, the whole system assumes that there is a single client per server - so you should not share a single server among serveral client.
|
||||
|
||||
How the Client and Server communicate between each other is defined using a RPC object.
|
||||
Out-of-the-box, Anselme provides RPC objects that can communicate over [LÖVE](https://www.love2d.org/) threads, and over [JSON-RPC 2.0](https://www.jsonrpc.org/specification); these can be easily created using the functions in [anselme.server](#anselme_server).
|
||||
If you want to implement a custom RPC mechanism, you can look at the existing implementations in `anselme/server/rpc/`.
|
||||
|
||||
Example usage in a LÖVE game:
|
||||
```lua
|
||||
local server = require("anselme.server")
|
||||
|
||||
-- create a new client+server
|
||||
local client = server.new_love_thread()
|
||||
client:load_stdlib()
|
||||
|
||||
-- load an anselme script file in a new branch
|
||||
local run_state = client:branch("block")
|
||||
run_state:run_file("script.ans")
|
||||
|
||||
-- start script
|
||||
run_state:step(handle_event)
|
||||
|
||||
-- callback to handle an anselme event
|
||||
function handle_event(event, data)
|
||||
if event == "text" then
|
||||
show_dialog_box {
|
||||
lines = data,
|
||||
on_dialog_box_closed = function()
|
||||
run_state:step(handle_event) -- resume script
|
||||
end
|
||||
}
|
||||
elseif event == "choice" then
|
||||
show_choice_dialog_box {
|
||||
choices = data,
|
||||
on_dialog_box_closed = function(choice_number)
|
||||
run_state:choose(choice_number)
|
||||
run_state:step(handle_event)
|
||||
end
|
||||
}
|
||||
elseif event == "return" then
|
||||
run_state:merge()
|
||||
run_state:remove() -- remove branch from server
|
||||
elseif event == "error" then
|
||||
print("error in anselme thread!", data)
|
||||
run_state:remove()
|
||||
end
|
||||
end
|
||||
|
||||
function love.update()
|
||||
client:process() -- handle messages coming from the server
|
||||
end
|
||||
```
|
||||
|
||||
# anselme.server
|
||||
|
||||
{{anselme/server/init.lua}}
|
||||
|
||||
# Client
|
||||
|
||||
{{anselme/server/Client.lua}}
|
||||
|
||||
# Server
|
||||
|
||||
{{anselme/server/Server.lua}}
|
||||
Loading…
Add table
Add a link
Reference in a new issue