summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2012-03-09 21:30:42 +0100
committerThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2012-03-10 18:07:29 +0100
commit78c912fb4007c3e5f0b43de02646772acb21ecf2 (patch)
treee45409ff061de75b0e4273763a87f5a25de6a65b /scripts
parent2fa7d1f39b24714ee6dc72b6b9e61ec5a1997724 (diff)
downloadmanaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.tar.gz
manaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.tar.xz
manaserv-78c912fb4007c3e5f0b43de02646772acb21ecf2.zip
Moved the managing of NPC script coroutines into C++
Rather than wrapping NPC functions up in coroutines in the Lua side, they are now managed on the C++ side as "script threads", which are essentially the same thing. The main purpose is that the server can now know whether any of these long running script interactions are still active, which will probably be useful when adding the ability to reload scripts. Reviewed-by: Erik Schilling
Diffstat (limited to 'scripts')
-rw-r--r--scripts/lua/libmana.lua268
1 files changed, 12 insertions, 256 deletions
diff --git a/scripts/lua/libmana.lua b/scripts/lua/libmana.lua
index cb92276..94d1f99 100644
--- a/scripts/lua/libmana.lua
+++ b/scripts/lua/libmana.lua
@@ -22,22 +22,9 @@ require "scripts/lua/libmana-constants"
local npc_talk_functs = {}
local npc_update_functs = {}
--- Table that associates to each Character pointer its state with respect to
--- NPCs (only one at a time). A state is an array with four fields:
--- . 1: pointer to the NPC the player is currently talking to.
--- . 2: coroutine running the NPC handler.
--- . 3: next event the NPC expects from the server.
--- (1 = npc_next, 2 = npc_choose, 3 = quest_reply, 4 = 1+3)
--- . 4: countdown (in minutes) before the state is deleted.
--- . 5: name of the expected quest variable. (optional)
-local states = {}
-
-- Array containing the function registered by atinit.
local init_fun = {}
--- Tick timer used during update to clean obsolete states.
-local timer
-
-- Creates an NPC and associates the given handler.
-- Note: Cannot be called until map initialization has started.
function create_npc(name, id, gender, x, y, talkfunct, updatefunct)
@@ -45,235 +32,31 @@ function create_npc(name, id, gender, x, y, talkfunct, updatefunct)
if talkfunct then
npc_talk_functs[npc] = function(npc, ch)
talkfunct(npc, ch)
- do_npc_close(npc, ch)
+ mana.npc_end(npc, ch)
end
end
if updatefunct then npc_update_functs[npc] = updatefunct end
return npc
end
--- Waits for the player to acknowledge the previous message, if any.
-function do_wait()
- coroutine.yield(0)
-end
-
--- Sends an npc message to a player.
--- Note: Does not wait for the player to acknowledge the message.
-function do_message(npc, ch, msg)
- -- Wait for the arrival of a pending acknowledgment, if any.
- coroutine.yield(0)
- mana.npc_message(npc, ch, msg)
- -- An acknowledgment is pending, but do not wait for its arrival.
- coroutine.yield(1)
-end
-
--- Sends an NPC question to a player and waits for its answer.
-function do_choice(npc, ch, ...)
- -- Wait for the arrival of a pending acknowledgment, if any.
- coroutine.yield(0)
- mana.npc_choice(npc, ch, ...)
- -- Wait for player choice.
- return coroutine.yield(2)
-end
-
--- Sends an NPC integer ask to a player and waits for its answer.
-function do_ask_integer(npc, ch, min_num, max_num, ...)
- -- Wait for the arrival of a pending acknowledgment, if any.
- coroutine.yield(0)
- mana.npc_ask_integer(npc, ch, min_num, max_num, ...)
- -- Wait for player answer.
- return coroutine.yield(2)
-end
-
--- Sends an NPC string ask to a player and waits for its answer.
-function do_ask_string(npc, ch)
- -- Wait for the arrival of a pending acknowledgment, if any.
- coroutine.yield(0)
- mana.npc_ask_string(npc, ch)
- -- Wait for player answer.
- return coroutine.yield(2)
-end
-
--- Sends an NPC request to send letter to a player and waits for them to
--- send the letter.
-function do_post(npc, ch)
- coroutine.yield(0)
- mana.npc_post(npc, ch)
- return coroutine.yield(1)
-end
-
--- Gets the value of a quest variable.
--- Calling this function while an acknowledment is pending is desirable, so
--- that lag cannot be perceived by the player.
-function get_quest_var(ch, name)
- -- Query the server and return immediatly if a value is available.
- local value = mana.chr_get_quest(ch, name)
- if value then return value end
- -- Wait for database reply.
- return coroutine.yield(3, name)
-end
-
--- Gets the post for a user.
-function getpost(ch)
- mana.chr_get_post(ch)
- return coroutine.yield(3)
-end
-
--- Processes as much of an NPC handler as possible.
-local function process_npc(w, ...)
- local co = w[2]
- local pending = (w[3] == 4)
- local first = true
- while true do
- local b, v, u
- if first then
- -- First time, resume with the arguments the coroutine was waiting for.
- b, v, u = coroutine.resume(co, ...)
- first = false
- else
- -- Otherwise, simply resume.
- b, v, u = coroutine.resume(co)
- end
-
- if not b then print("LUA error: ", v)end
-
- if not b or not v then
- -- Either there was an error, or the handler just finished its work.
- return
- elseif v == 2 then
- -- The coroutine needs a user choice from the server, so wait for it.
- w[3] = 2
- break
- elseif v == 3 then
- -- The coroutine needs the value of a quest variable from the server.
- w[5] = u
- if pending then
- -- The coroutine has also sent a message to the user, so do not
- -- forget about it, as it would flood the user with new messages.
- w[3] = 4
- else
- w[3] = 3
- end
- break
- elseif pending then
- -- The coroutine is about to interact with the user. But the previous
- -- action has not been acknowledged by the user yet, so wait for it.
- w[3] = 1
- break
- elseif v == 1 then
- -- A message has just been sent. But the coroutine can keep going in case
- -- there is still some work to do while waiting for user acknowledgment.
- pending = true
- end
- end
- -- Restore the countdown, as there was some activity.
- w[4] = 5
- return true
-end
+-- These are deprecated and only provided for compatibility!
+do_message = mana.npc_message
+do_choice = mana.npc_choice
+do_ask_integer = mana.npc_ask_integer
+do_ask_string = mana.npc_ask_string
+do_post = mana.npc_post
+get_quest_var = mana.chr_get_quest
+getpost = mana.chr_get_post
-- Registered as the function to call whenever a player starts talking to an
--- NPC. Creates a coroutine based on the registered NPC handler.
+-- NPC. Calls the registered NPC handler.
local function npc_start(npc, ch)
- states[ch] = nil
local h = npc_talk_functs[npc]
- if not h then return end
- local w = { npc, coroutine.create(h) }
- if process_npc(w, npc, ch) then
- states[ch] = w
- if not timer then
- timer = 600
- end
- end
- -- coroutine.resume(w)
- -- do_npc_close(npc, ch)
-end
-
-function do_npc_close(npc, ch)
- mana.npc_end(npc, ch)
-end
-
--- Registered as the function to call whenever a player continues talking to an
--- NPC. Checks that the NPC expects it, and processes the respective coroutine.
-local function npc_next(npc, ch)
- local w = states[ch]
- if w then
- local w3 = w[3]
- if w3 == 4 then
- w[3] = 3
- return
- elseif w3 == 1 and process_npc(w) then
- return
- end
- end
- states[ch] = nil
-end
-
--- Registered as the function to call whenever a player selects a particular
--- reply. Checks that the NPC expects it, and processes the respective
--- coroutine.
-local function npc_choose(npc, ch, u)
- local w = states[ch]
- if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then
- states[ch] = nil
- end
-end
-
-local function npc_integer(npc, ch, u)
- local w = states[ch]
- if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then
- states[ch] = nil
+ if h then
+ h(npc, ch)
end
end
-local function npc_string(npc, ch, u)
- local w = states[ch]
- if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then
- states[ch] = nil
- end
-end
-
--- Called by the game when a player sends a letter.
--- TODO: Actually this function isn't called, probably unfinished implementation
-local function npc_post(npc, ch, sender, letter)
- local w = states[ch]
- if not (w and w[1] == npc and w[3] == 1 and process_npc(w, sender, letter)) then
- states[ch] = nil
- end
-end
-
--- Registered as the function to call whenever a value of a quest variable is
--- retrieved. Checks that the NPC expects it, and processes the respective
--- coroutine.
--- Note: the check for NPC correctness is missing, but it should never matter.
-local function npc_quest_reply(ch, name, value)
- local w = states[ch]
- if w then
- local w3 = w[3]
- if (w3 == 3 or w3 == 4) and w[5] == name then
- w[5] = nil
- if process_npc(w, value) then
- return
- end
- end
- end
- states[ch] = nil
-end
-
--- Registered as the function to call whenever the server has recovered a
--- post for a user.
-local function npc_post_reply(ch, sender, letter)
- local w = states[ch]
- if w then
- local w3 = w[3]
- if (w3 == 3 or w3 == 4) then
- if process_npc(w, sender, letter) then
- return
- end
- end
- end
- states[ch] = nil
-end
-
-- Registered as the function to call every tick for each NPC.
local function npc_update(npc)
local h = npc_update_functs[npc];
@@ -308,28 +91,7 @@ end
-- Registered as the function to call every tick.
-- Checks for scheduled function calls and cleans obsolete connections.
local function update()
- -- check the scheduler
check_schedule()
-
- -- Run every minute only, in order not to overload the server.
- if not timer then return end
- timer = timer - 1
- if timer ~= 0 then return end
- -- Free connections that have been inactive for 3-4 minutes.
- for k, w in pairs(states) do
- local t = w[4] - 1
- if t == 0 then
- states[k] = nil
- else
- w[4] = t
- end
- end
- -- Restart timer if there are still some pending states.
- if next(states) then
- timer = 600
- else
- timer = nil
- end
end
-- Registers a function so that is is executed during map initialization.
@@ -459,12 +221,6 @@ end
mana.on_update(update)
mana.on_npc_start(npc_start)
-mana.on_npc_next(npc_next)
-mana.on_npc_choose(npc_choose)
-mana.on_npc_integer(npc_integer)
-mana.on_npc_string(npc_string)
-mana.on_npc_quest_reply(npc_quest_reply)
-mana.on_npc_post_reply(npc_post_reply)
mana.on_npc_update(npc_update)
mana.on_create_npc_delayed(create_npc_delayed)