Skill (#9)
* TriggerSkill * update type comment * events of phases * lua annotation * change style of enums * clear lua stack * multi lua_State at server side * disable addPlayer when room is full or started * logic:trigger Co-authored-by: Notify-ctrl <notify-ctrl@qq.com>
This commit is contained in:
parent
daae2466ae
commit
4e25c032e6
|
@ -1,36 +1,33 @@
|
|||
---@class Card : Object
|
||||
---@field package Package
|
||||
---@field name string
|
||||
---@field suit number # enum suit
|
||||
---@field number number
|
||||
---@field color number # enum color
|
||||
---@field id number
|
||||
---@field type number # enum type
|
||||
---@field suit Suit
|
||||
---@field number integer
|
||||
---@field color Color
|
||||
---@field id integer
|
||||
---@field type CardType
|
||||
local Card = class("Card")
|
||||
|
||||
-- enum Suit
|
||||
fk.createEnum(Card, {
|
||||
"Spade",
|
||||
"Club",
|
||||
"Heart",
|
||||
"Diamond",
|
||||
"NoSuit"
|
||||
})
|
||||
---@alias Suit integer
|
||||
|
||||
-- enum Color
|
||||
fk.createEnum(Card, {
|
||||
"Black",
|
||||
"Red",
|
||||
"NoColor"
|
||||
})
|
||||
Card.Spade = 1
|
||||
Card.Club = 2
|
||||
Card.Heart = 3
|
||||
Card.Diamond = 4
|
||||
Card.NoSuit = 5
|
||||
|
||||
-- enum Type
|
||||
fk.createEnum(Card, {
|
||||
"TypeSkill",
|
||||
"TypeBasic",
|
||||
"TypeTrick",
|
||||
"TypeEquip"
|
||||
})
|
||||
---@alias Color integer
|
||||
|
||||
Card.Black = 1
|
||||
Card.Red = 2
|
||||
Card.NoColor = 3
|
||||
|
||||
---@alias CardType integer
|
||||
|
||||
Card.TypeSkill = 1
|
||||
Card.TypeBasic = 2
|
||||
Card.TypeTrick = 3
|
||||
Card.TypeEquip = 4
|
||||
|
||||
function Card:initialize(name, suit, number, color)
|
||||
self.name = name
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
---@diagnostic disable: lowercase-global
|
||||
inspect = require "inspect"
|
||||
|
||||
DebugMode = true
|
||||
function PrintWhenMethodCall()
|
||||
local info = debug.getinfo(2)
|
||||
local name = info.name
|
||||
local line = info.currentline
|
||||
local namewhat = info.namewhat
|
||||
local shortsrc = info.short_src
|
||||
if (namewhat == "method") and
|
||||
(shortsrc ~= "[C]") and
|
||||
(not string.find(shortsrc, "/lib")) then
|
||||
print(shortsrc .. ":" .. line .. ": " .. name)
|
||||
end
|
||||
end
|
||||
--debug.sethook(PrintWhenMethodCall, "c")
|
||||
|
||||
function p(v) print(inspect(v)) end
|
|
@ -1,11 +1,12 @@
|
|||
---@class Engine : Object
|
||||
---@field packages table
|
||||
---@field skills table
|
||||
---@field related_skills table
|
||||
---@field generals table
|
||||
---@field lords table
|
||||
---@field cards table
|
||||
---@field translations table
|
||||
---@field packages table<string, Package>
|
||||
---@field skills table<string, Skill>
|
||||
---@field related_skills table<string, Skill[]>
|
||||
---@field global_trigger TriggerSkill[]
|
||||
---@field generals table<string, General>
|
||||
---@field lords string[]
|
||||
---@field cards Card[]
|
||||
---@field translations table<string, string>
|
||||
local Engine = class("Engine")
|
||||
|
||||
function Engine:initialize()
|
||||
|
@ -19,7 +20,8 @@ function Engine:initialize()
|
|||
|
||||
self.packages = {} -- name --> Package
|
||||
self.skills = {} -- name --> Skill
|
||||
self.related_skills = {} -- skillName --> relatedName
|
||||
self.related_skills = {} -- skillName --> relatedSkill[]
|
||||
self.global_trigger = {}
|
||||
self.generals = {} -- name --> General
|
||||
self.lords = {} -- lordName[]
|
||||
self.cards = {} -- Card[]
|
||||
|
@ -61,7 +63,7 @@ function Engine:loadTranslationTable(t)
|
|||
end
|
||||
end
|
||||
|
||||
---@param skill any
|
||||
---@param skill Skill
|
||||
function Engine:addSkill(skill)
|
||||
assert(skill.class:isSubclassOf(Skill))
|
||||
if self.skills[skill.name] ~= nil then
|
||||
|
@ -70,7 +72,7 @@ function Engine:addSkill(skill)
|
|||
self.skills[skill.name] = skill
|
||||
end
|
||||
|
||||
---@param skills any[]
|
||||
---@param skills Skill[]
|
||||
function Engine:addSkills(skills)
|
||||
assert(type(skills) == "table")
|
||||
for _, skill in ipairs(skills) do
|
||||
|
@ -111,7 +113,7 @@ function Engine:addCards(cards)
|
|||
end
|
||||
end
|
||||
|
||||
---@param num number
|
||||
---@param num integer
|
||||
---@param generalPool General[]
|
||||
---@param except string[]
|
||||
---@param filter function
|
||||
|
@ -123,7 +125,7 @@ function Engine:getGeneralsRandomly(num, generalPool, except, filter)
|
|||
|
||||
generalPool = generalPool or self.generals
|
||||
except = except or {}
|
||||
|
||||
|
||||
local availableGenerals = {}
|
||||
for _, general in pairs(generalPool) do
|
||||
if not table.contains(except, general.name) and not (filter and filter(general)) then
|
||||
|
|
|
@ -2,20 +2,19 @@
|
|||
---@field package Package
|
||||
---@field name string
|
||||
---@field kingdom string
|
||||
---@field hp number
|
||||
---@field maxHp number
|
||||
---@field gender number
|
||||
---@field skills table
|
||||
---@field other_skills table
|
||||
---@field hp integer
|
||||
---@field maxHp integer
|
||||
---@field gender Gender
|
||||
---@field skills Skill[]
|
||||
---@field other_skills string[]
|
||||
General = class("General")
|
||||
|
||||
-- enum Gender
|
||||
fk.createEnum(General, {
|
||||
"Male",
|
||||
"Female"
|
||||
})
|
||||
---@alias Gender integer
|
||||
|
||||
function General:initialize(package, name, kingdom, hp, maxHp, gender, initialHp)
|
||||
General.Male = 1
|
||||
General.Female = 2
|
||||
|
||||
function General:initialize(package, name, kingdom, hp, maxHp, gender)
|
||||
self.package = package
|
||||
self.name = name
|
||||
self.kingdom = kingdom
|
||||
|
@ -23,12 +22,11 @@ function General:initialize(package, name, kingdom, hp, maxHp, gender, initialHp
|
|||
self.maxHp = maxHp or hp
|
||||
self.gender = gender or General.Male
|
||||
|
||||
self.skills = {} -- Skill[]
|
||||
-- skill belongs other general, e.g. "mashu" of pangde
|
||||
self.other_skills = {} -- string[]
|
||||
self.skills = {} -- skills first added to this general
|
||||
self.other_skills = {} -- skill belongs other general, e.g. "mashu" of pangde
|
||||
end
|
||||
|
||||
---@param skill any
|
||||
---@param skill Skill
|
||||
function General:addSkill(skill)
|
||||
if (type(skill) == "string") then
|
||||
table.insert(self.other_skills, skill)
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
---@class Package : Object
|
||||
---@field name string
|
||||
---@field type number
|
||||
---@field generals table
|
||||
---@field extra_skills table
|
||||
---@field related_skills table
|
||||
---@field cards table
|
||||
---@field type PackageType
|
||||
---@field generals General[]
|
||||
---@field extra_skills Skill[]
|
||||
---@field related_skills table<string, string>
|
||||
---@field cards Card[]
|
||||
local Package = class("Package")
|
||||
|
||||
-- enum Type
|
||||
fk.createEnum(Package, {
|
||||
"GeneralPack",
|
||||
"CardPack",
|
||||
"SpecialPack"
|
||||
})
|
||||
---@alias PackageType integer
|
||||
|
||||
Package.GeneralPack = 1
|
||||
Package.CardPack = 2
|
||||
Package.SpecialPack = 3
|
||||
|
||||
function Package:initialize(name, _type)
|
||||
assert(type(name) == "string")
|
||||
|
@ -21,11 +20,9 @@ function Package:initialize(name, _type)
|
|||
self.type = _type or Package.GeneralPack
|
||||
|
||||
self.generals = {}
|
||||
-- skill not belongs to any generals, like "jixi"
|
||||
self.extra_skills = {}
|
||||
-- table: string --> string
|
||||
self.extra_skills = {} -- skill not belongs to any generals, like "jixi"
|
||||
self.related_skills = {}
|
||||
self.cards = {} --> Card[]
|
||||
self.cards = {}
|
||||
end
|
||||
|
||||
---@return table skills
|
||||
|
|
|
@ -1,6 +1,32 @@
|
|||
---@class Player : Object
|
||||
---@field hp integer
|
||||
---@field maxHp integer
|
||||
---@field kingdom string
|
||||
---@field role string
|
||||
---@field general string
|
||||
---@field handcard_num integer
|
||||
---@field seat integer
|
||||
---@field phase Phase
|
||||
---@field faceup boolean
|
||||
---@field chained boolean
|
||||
---@field dying boolean
|
||||
---@field dead boolean
|
||||
---@field state string
|
||||
---@field player_skills Skill[]
|
||||
local Player = class("Player")
|
||||
|
||||
---@alias Phase integer
|
||||
|
||||
Player.RoundStart = 1
|
||||
Player.Start = 2
|
||||
Player.Judge = 3
|
||||
Player.Draw = 4
|
||||
Player.Play = 5
|
||||
Player.Discard = 6
|
||||
Player.Finish = 7
|
||||
Player.NotActive = 8
|
||||
Player.PhaseNone = 9
|
||||
|
||||
function Player:initialize()
|
||||
self.hp = 0
|
||||
self.maxHp = 0
|
||||
|
@ -15,8 +41,8 @@ function Player:initialize()
|
|||
self.dying = false
|
||||
self.dead = false
|
||||
self.state = ""
|
||||
|
||||
self.playerSkills = {}
|
||||
|
||||
self.player_skills = {}
|
||||
end
|
||||
|
||||
---@param general General
|
||||
|
|
|
@ -1,30 +1,22 @@
|
|||
---@class Skill
|
||||
---@class Skill : Object
|
||||
---@field name string
|
||||
---@field frequency Frequency
|
||||
---@field visible boolean
|
||||
local Skill = class("Skill")
|
||||
|
||||
fk.createEnum(Skill, {
|
||||
"Common",
|
||||
"Frequent",
|
||||
"Compulsory",
|
||||
"Awaken",
|
||||
"Limit",
|
||||
})
|
||||
---@alias Frequency integer
|
||||
|
||||
function Skill:initialize(name, skillType)
|
||||
Skill.Frequent = 1
|
||||
Skill.NotFrequent = 2
|
||||
Skill.Compulsory = 3
|
||||
Skill.Limited = 4
|
||||
Skill.Wake = 5
|
||||
|
||||
function Skill:initialize(name, frequency)
|
||||
-- TODO: visible, lord, etc
|
||||
self.name = name
|
||||
self.description = ":" .. name
|
||||
self.skillType = skillType
|
||||
end
|
||||
|
||||
local TriggerSkill = class("TriggerSkill", Skill)
|
||||
|
||||
function TriggerSkill:initialize(spec)
|
||||
Skill.initialize(self, spec.name, spec.skillType)
|
||||
self.isRefreshAt = spec.isRefreshAt
|
||||
self.isTriggerable = spec.isTriggerable
|
||||
self.targetFilter = spec.targetFilter
|
||||
self.cardFilter = spec.cardFilter
|
||||
self.beforeTrigger = spec.beforeTrigger
|
||||
self.onTrigger = spec.onTrigger
|
||||
self.frequency = frequency
|
||||
self.visible = true
|
||||
end
|
||||
|
||||
return Skill
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
---@class TriggerSkill : Skill
|
||||
---@field global boolean
|
||||
---@field events Event[]
|
||||
---@field refresh_events Event[]
|
||||
---@field priority_table table<Event, number>
|
||||
local TriggerSkill = Skill:subclass("TriggerSkill")
|
||||
|
||||
function TriggerSkill:initialize(name, frequency)
|
||||
Skill.initialize(self, name, frequency)
|
||||
|
||||
self.global = false
|
||||
self.events = {}
|
||||
self.refresh_events = {}
|
||||
self.priority_table = {} -- GameEvent --> priority
|
||||
end
|
||||
|
||||
-- Default functions
|
||||
|
||||
---Determine whether a skill can refresh at this moment
|
||||
---@param event Event # TriggerEvent
|
||||
---@param target ServerPlayer # Player who triggered this event
|
||||
---@param player ServerPlayer # Player who is operating
|
||||
---@param data any # useful data of the event
|
||||
---@return nil
|
||||
function TriggerSkill:canRefresh(event, target, player, data) return false end
|
||||
|
||||
---Refresh the skill (e.g. clear marks)
|
||||
---@param event Event # TriggerEvent
|
||||
---@param target ServerPlayer # Player who triggered this event
|
||||
---@param player ServerPlayer # Player who is operating
|
||||
---@param data any # useful data of the event
|
||||
function TriggerSkill:refresh(event, target, player, data) end
|
||||
|
||||
---Determine whether a skill can trigger at this moment
|
||||
---@param event Event # TriggerEvent
|
||||
---@param target ServerPlayer # Player who triggered this event
|
||||
---@param player ServerPlayer # Player who is operating
|
||||
---@param data any # useful data of the event
|
||||
---@return boolean
|
||||
function TriggerSkill:triggerable(event, target, player, data)
|
||||
return target and (target == player)
|
||||
and (self.global or (target:isAlive() and target:hasSkill(self)))
|
||||
end
|
||||
|
||||
---Trigger this skill
|
||||
---@param event Event # TriggerEvent
|
||||
---@param target ServerPlayer # Player who triggered this event
|
||||
---@param player ServerPlayer # Player who is operating
|
||||
---@param data any # useful data of the event
|
||||
---@return boolean # returns true if trigger is broken
|
||||
function TriggerSkill:trigger(event, target, player, data)
|
||||
print(string.format("%s triggered: event=%d", self.name, event))
|
||||
--if player.room:askForSkillInvoke(self.name) then
|
||||
-- return self:use(event, target, player, data)
|
||||
--end
|
||||
return false
|
||||
end
|
||||
|
||||
---Use this skill
|
||||
---@param event Event # TriggerEvent
|
||||
---@param target ServerPlayer # Player who triggered this event
|
||||
---@param player ServerPlayer # Player who is operating
|
||||
---@param data any # useful data of the event
|
||||
---@return boolean
|
||||
function TriggerSkill:use(event, target, player, data) end
|
||||
|
||||
return TriggerSkill
|
|
@ -29,6 +29,26 @@ function table:insertTable(list)
|
|||
end
|
||||
end
|
||||
|
||||
function table:indexOf(value, from)
|
||||
from = from or 1
|
||||
for i = from, #self do
|
||||
if self[i] == value then return i end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function table:removeOne(element)
|
||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||
|
||||
for i = 1, #self do
|
||||
if self[i] == element then
|
||||
table.remove(self, i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@class Sql
|
||||
Sql = {
|
||||
---@param filename string
|
||||
|
@ -95,23 +115,15 @@ function Stack:pop()
|
|||
return self.t[self.p + 1]
|
||||
end
|
||||
|
||||
function table:removeOne(element)
|
||||
if #self == 0 or type(self[1]) ~= type(element) then return false end
|
||||
|
||||
for i = 1, #self do
|
||||
if self[i] == element then
|
||||
table.remove(self, i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- convert a table to enum, e.g. core/card.lua
|
||||
---@param table table # class to store the enum
|
||||
--- useful function to create enums
|
||||
---
|
||||
--- only use it in a terminal
|
||||
---@param table string
|
||||
---@param enum string[]
|
||||
function fk.createEnum(table, enum)
|
||||
function CreateEnum(table, enum)
|
||||
local enum_format = "%s.%s = %d"
|
||||
for i, v in ipairs(enum) do
|
||||
table[v] = i
|
||||
print(string.format(enum_format, table, v, i))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
-- load types for extension
|
||||
|
||||
SkillCard = require "core.card_type.skill"
|
||||
BasicCard = require "core.card_type.basic"
|
||||
TrickCard = require "core.card_type.trick"
|
||||
EquipCard = require "core.card_type.equip"
|
||||
|
||||
dofile "lua/server/event.lua"
|
||||
TriggerSkill = require "core.skill_type.trigger"
|
||||
|
||||
---@param spec table
|
||||
---@return BasicCard
|
||||
function fk.CreateBasicCard(spec)
|
||||
|
@ -36,3 +46,58 @@ function fk.CreateEquipCard(spec)
|
|||
local card = EquipCard:new(spec.name, spec.suit, spec.number)
|
||||
return card
|
||||
end
|
||||
|
||||
---@param spec table
|
||||
---@return TriggerSkill
|
||||
function fk.CreateTriggerSkill(spec)
|
||||
assert(type(spec.name) == "string")
|
||||
--assert(type(spec.on_trigger) == "function")
|
||||
if spec.frequency then assert(type(spec.frequency) == "number") end
|
||||
|
||||
local frequency = spec.frequency or Skill.NotFrequent
|
||||
local skill = TriggerSkill:new(spec.name, frequency)
|
||||
|
||||
if type(spec.events) == "number" then
|
||||
table.insert(skill.events, spec.events)
|
||||
elseif type(spec.events) == "table" then
|
||||
table.insertTable(skill.events, spec.events)
|
||||
end
|
||||
|
||||
if type(spec.refresh_events) == "number" then
|
||||
table.insert(skill.refresh_events, spec.refresh_events)
|
||||
elseif type(spec.refresh_events) == "table" then
|
||||
table.insertTable(skill.refresh_events, spec.refresh_events)
|
||||
end
|
||||
|
||||
if type(spec.global) == "boolean" then skill.global = spec.global end
|
||||
|
||||
if spec.on_trigger then skill.trigger = spec.on_trigger end
|
||||
|
||||
if spec.can_trigger then
|
||||
skill.triggerable = spec.can_trigger
|
||||
end
|
||||
|
||||
if spec.can_refresh then
|
||||
skill.canRefresh = spec.can_refresh
|
||||
end
|
||||
|
||||
if not spec.priority then
|
||||
if frequency == Skill.Wake then
|
||||
spec.priority = 3
|
||||
elseif frequency == Skill.Compulsory then
|
||||
spec.priority = 2
|
||||
else
|
||||
spec.priority = 1
|
||||
end
|
||||
end
|
||||
if type(spec.priority) == "number" then
|
||||
for _, event in ipairs(spec.events) do
|
||||
skill.priority_table[event] = spec.priority
|
||||
end
|
||||
elseif type(spec.priority) == "table" then
|
||||
for event, priority in pairs(spec.priority) do
|
||||
skill.priority_table[event] = priority
|
||||
end
|
||||
end
|
||||
return skill
|
||||
end
|
||||
|
|
|
@ -11,26 +11,15 @@ json = require "json"
|
|||
|
||||
dofile "lua/lib/sha256.lua"
|
||||
dofile "lua/core/util.lua"
|
||||
dofile "lua/core/debug.lua"
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
DebugMode = true
|
||||
|
||||
function pt(t)
|
||||
for k, v in pairs(t) do
|
||||
print(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
-- load core classes
|
||||
Engine = require "core.engine"
|
||||
Package = require "core.package"
|
||||
General = require "core.general"
|
||||
Card = require "core.card"
|
||||
SkillCard = require "core.card_type.skill"
|
||||
BasicCard = require "core.card_type.basic"
|
||||
TrickCard = require "core.card_type.trick"
|
||||
EquipCard = require "core.card_type.equip"
|
||||
Skill = require "core.skill"
|
||||
Player = require "core.player"
|
||||
|
||||
|
|
|
@ -0,0 +1,337 @@
|
|||
local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
|
||||
local inspect = {Options = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inspect._VERSION = 'inspect.lua 3.1.0'
|
||||
inspect._URL = 'http://github.com/kikito/inspect.lua'
|
||||
inspect._DESCRIPTION = 'human-readable representations of tables'
|
||||
inspect._LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2022 Enrique García Cota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
inspect.KEY = setmetatable({}, { __tostring = function() return 'inspect.KEY' end })
|
||||
inspect.METATABLE = setmetatable({}, { __tostring = function() return 'inspect.METATABLE' end })
|
||||
|
||||
local tostring = tostring
|
||||
local rep = string.rep
|
||||
local match = string.match
|
||||
local char = string.char
|
||||
local gsub = string.gsub
|
||||
local fmt = string.format
|
||||
|
||||
local function rawpairs(t)
|
||||
return next, t, nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function smartQuote(str)
|
||||
if match(str, '"') and not match(str, "'") then
|
||||
return "'" .. str .. "'"
|
||||
end
|
||||
return '"' .. gsub(str, '"', '\\"') .. '"'
|
||||
end
|
||||
|
||||
|
||||
local shortControlCharEscapes = {
|
||||
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
|
||||
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\127"] = "\\127",
|
||||
}
|
||||
local longControlCharEscapes = { ["\127"] = "\127" }
|
||||
for i = 0, 31 do
|
||||
local ch = char(i)
|
||||
if not shortControlCharEscapes[ch] then
|
||||
shortControlCharEscapes[ch] = "\\" .. i
|
||||
longControlCharEscapes[ch] = fmt("\\%03d", i)
|
||||
end
|
||||
end
|
||||
|
||||
local function escape(str)
|
||||
return (gsub(gsub(gsub(str, "\\", "\\\\"),
|
||||
"(%c)%f[0-9]", longControlCharEscapes),
|
||||
"%c", shortControlCharEscapes))
|
||||
end
|
||||
|
||||
local function isIdentifier(str)
|
||||
return type(str) == "string" and not not str:match("^[_%a][_%a%d]*$")
|
||||
end
|
||||
|
||||
local flr = math.floor
|
||||
local function isSequenceKey(k, sequenceLength)
|
||||
return type(k) == "number" and
|
||||
flr(k) == k and
|
||||
1 <= (k) and
|
||||
k <= sequenceLength
|
||||
end
|
||||
|
||||
local defaultTypeOrders = {
|
||||
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
|
||||
['function'] = 5, ['userdata'] = 6, ['thread'] = 7,
|
||||
}
|
||||
|
||||
local function sortKeys(a, b)
|
||||
local ta, tb = type(a), type(b)
|
||||
|
||||
|
||||
if ta == tb and (ta == 'string' or ta == 'number') then
|
||||
return (a) < (b)
|
||||
end
|
||||
|
||||
local dta = defaultTypeOrders[ta] or 100
|
||||
local dtb = defaultTypeOrders[tb] or 100
|
||||
|
||||
|
||||
return dta == dtb and ta < tb or dta < dtb
|
||||
end
|
||||
|
||||
local function getKeys(t)
|
||||
|
||||
local seqLen = 1
|
||||
while rawget(t, seqLen) ~= nil do
|
||||
seqLen = seqLen + 1
|
||||
end
|
||||
seqLen = seqLen - 1
|
||||
|
||||
local keys, keysLen = {}, 0
|
||||
for k in rawpairs(t) do
|
||||
if not isSequenceKey(k, seqLen) then
|
||||
keysLen = keysLen + 1
|
||||
keys[keysLen] = k
|
||||
end
|
||||
end
|
||||
table.sort(keys, sortKeys)
|
||||
return keys, keysLen, seqLen
|
||||
end
|
||||
|
||||
local function countCycles(x, cycles)
|
||||
if type(x) == "table" then
|
||||
if cycles[x] then
|
||||
cycles[x] = cycles[x] + 1
|
||||
else
|
||||
cycles[x] = 1
|
||||
for k, v in rawpairs(x) do
|
||||
countCycles(k, cycles)
|
||||
countCycles(v, cycles)
|
||||
end
|
||||
countCycles(getmetatable(x), cycles)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function makePath(path, a, b)
|
||||
local newPath = {}
|
||||
local len = #path
|
||||
for i = 1, len do newPath[i] = path[i] end
|
||||
|
||||
newPath[len + 1] = a
|
||||
newPath[len + 2] = b
|
||||
|
||||
return newPath
|
||||
end
|
||||
|
||||
|
||||
local function processRecursive(process,
|
||||
item,
|
||||
path,
|
||||
visited)
|
||||
if item == nil then return nil end
|
||||
if visited[item] then return visited[item] end
|
||||
|
||||
local processed = process(item, path)
|
||||
if type(processed) == "table" then
|
||||
local processedCopy = {}
|
||||
visited[item] = processedCopy
|
||||
local processedKey
|
||||
|
||||
for k, v in rawpairs(processed) do
|
||||
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
|
||||
if processedKey ~= nil then
|
||||
processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
|
||||
end
|
||||
end
|
||||
|
||||
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
|
||||
if type(mt) ~= 'table' then mt = nil end
|
||||
setmetatable(processedCopy, mt)
|
||||
processed = processedCopy
|
||||
end
|
||||
return processed
|
||||
end
|
||||
|
||||
local function puts(buf, str)
|
||||
buf.n = buf.n + 1
|
||||
buf[buf.n] = str
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Inspector = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local Inspector_mt = { __index = Inspector }
|
||||
|
||||
local function tabify(inspector)
|
||||
puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
|
||||
end
|
||||
|
||||
function Inspector:getId(v)
|
||||
local id = self.ids[v]
|
||||
local ids = self.ids
|
||||
if not id then
|
||||
local tv = type(v)
|
||||
id = (ids[tv] or 0) + 1
|
||||
ids[v], ids[tv] = id, id
|
||||
end
|
||||
return tostring(id)
|
||||
end
|
||||
|
||||
function Inspector:putValue(v)
|
||||
local buf = self.buf
|
||||
local tv = type(v)
|
||||
if tv == 'string' then
|
||||
puts(buf, smartQuote(escape(v)))
|
||||
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
|
||||
tv == 'cdata' or tv == 'ctype' then
|
||||
puts(buf, tostring(v))
|
||||
elseif tv == 'table' and not self.ids[v] then
|
||||
local t = v
|
||||
|
||||
if t == inspect.KEY or t == inspect.METATABLE then
|
||||
puts(buf, tostring(t))
|
||||
elseif self.level >= self.depth then
|
||||
puts(buf, '{...}')
|
||||
else
|
||||
if self.cycles[t] > 1 then puts(buf, fmt('<%d>', self:getId(t))) end
|
||||
|
||||
local keys, keysLen, seqLen = getKeys(t)
|
||||
|
||||
puts(buf, '{')
|
||||
self.level = self.level + 1
|
||||
|
||||
for i = 1, seqLen + keysLen do
|
||||
if i > 1 then puts(buf, ',') end
|
||||
if i <= seqLen then
|
||||
puts(buf, ' ')
|
||||
self:putValue(t[i])
|
||||
else
|
||||
local k = keys[i - seqLen]
|
||||
tabify(self)
|
||||
if isIdentifier(k) then
|
||||
puts(buf, k)
|
||||
else
|
||||
puts(buf, "[")
|
||||
self:putValue(k)
|
||||
puts(buf, "]")
|
||||
end
|
||||
puts(buf, ' = ')
|
||||
self:putValue(t[k])
|
||||
end
|
||||
end
|
||||
|
||||
local mt = getmetatable(t)
|
||||
if type(mt) == 'table' then
|
||||
if seqLen + keysLen > 0 then puts(buf, ',') end
|
||||
tabify(self)
|
||||
puts(buf, '<metatable> = ')
|
||||
self:putValue(mt)
|
||||
end
|
||||
|
||||
self.level = self.level - 1
|
||||
|
||||
if keysLen > 0 or type(mt) == 'table' then
|
||||
tabify(self)
|
||||
elseif seqLen > 0 then
|
||||
puts(buf, ' ')
|
||||
end
|
||||
|
||||
puts(buf, '}')
|
||||
end
|
||||
|
||||
else
|
||||
puts(buf, fmt('<%s %d>', tv, self:getId(v)))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function inspect.inspect(root, options)
|
||||
options = options or {}
|
||||
|
||||
local depth = options.depth or (math.huge)
|
||||
local newline = options.newline or '\n'
|
||||
local indent = options.indent or ' '
|
||||
local process = options.process
|
||||
|
||||
if process then
|
||||
root = processRecursive(process, root, {}, {})
|
||||
end
|
||||
|
||||
local cycles = {}
|
||||
countCycles(root, cycles)
|
||||
|
||||
local inspector = setmetatable({
|
||||
buf = { n = 0 },
|
||||
ids = {},
|
||||
cycles = cycles,
|
||||
depth = depth,
|
||||
level = 0,
|
||||
newline = newline,
|
||||
indent = indent,
|
||||
}, Inspector_mt)
|
||||
|
||||
inspector:putValue(root)
|
||||
|
||||
return table.concat(inspector.buf)
|
||||
end
|
||||
|
||||
setmetatable(inspect, {
|
||||
__call = function(_, root, options)
|
||||
return inspect.inspect(root, options)
|
||||
end,
|
||||
})
|
||||
|
||||
return inspect
|
|
@ -1,16 +1,11 @@
|
|||
local EVENTS = {
|
||||
"GameStart",
|
||||
---@alias Event integer
|
||||
|
||||
"PhaseChanging",
|
||||
"PhaseStart",
|
||||
"PhaseProceeding",
|
||||
"PhaseEnd",
|
||||
|
||||
"PreCardUse",
|
||||
"AfterCardUseDeclared",
|
||||
"AfterCardTargetDeclared",
|
||||
"CardUsing",
|
||||
"CardUseFinished",
|
||||
}
|
||||
|
||||
GameEvent = Util:createEnum(EVENTS)
|
||||
fk.NonTrigger = 1
|
||||
fk.GameStart = 2
|
||||
fk.TurnStart = 3
|
||||
fk.EventPhaseStart = 4
|
||||
fk.EventPhaseProceeding = 5
|
||||
fk.EventPhaseEnd = 6
|
||||
fk.EventPhaseChanging = 7
|
||||
fk.EventPhaseSkipping = 8
|
||||
fk.NumOfEvents = 9
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
---@class GameLogic: Object
|
||||
---@field room Room
|
||||
---@field skill_table table
|
||||
---@filed skills table
|
||||
---@field skill_table table<Event, TriggerSkill[]>
|
||||
---@field refresh_skill_table table<Event, TriggerSkill[]>
|
||||
---@field skills string[]
|
||||
---@field event_stack Stack
|
||||
---@field role_table table
|
||||
---@field role_table string[][]
|
||||
local GameLogic = class("GameLogic")
|
||||
|
||||
function GameLogic:initialize(room)
|
||||
self.room = room
|
||||
self.skill_table = {} -- TriggerEvent --> Skill[]
|
||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||
self.refresh_skill_table = {}
|
||||
self.skills = {} -- skillName[]
|
||||
self.event_stack = Stack:new()
|
||||
|
||||
|
@ -62,6 +64,7 @@ function GameLogic:chooseGenerals()
|
|||
local lord = room:getLord()
|
||||
local lord_general = nil
|
||||
if lord ~= nil then
|
||||
room.current = lord
|
||||
local generals = Fk:getGeneralsRandomly(3)
|
||||
for i = 1, #generals do
|
||||
generals[i] = generals[i].name
|
||||
|
@ -99,7 +102,7 @@ end
|
|||
function GameLogic:prepareForStart()
|
||||
local room = self.room
|
||||
local players = room.players
|
||||
room.alive_players = players
|
||||
room.alive_players = {table.unpack(players)}
|
||||
for i = 1, #players - 1 do
|
||||
players[i].next = players[i + 1]
|
||||
end
|
||||
|
@ -127,11 +130,141 @@ function GameLogic:prepareForStart()
|
|||
-- TODO: prepare drawPile
|
||||
-- TODO: init cards in drawPile
|
||||
|
||||
-- TODO: init trigger table for self
|
||||
self:addTriggerSkill(GameRule)
|
||||
for _, trig in ipairs(Fk.global_trigger) do
|
||||
self:addTriggerSkill(trig)
|
||||
end
|
||||
end
|
||||
|
||||
function GameLogic:action()
|
||||
self:trigger(fk.GameStart)
|
||||
local room = self.room
|
||||
while true do
|
||||
self:trigger(fk.TurnStart, room.current)
|
||||
if room.game_finished then break end
|
||||
room.current = room.current:getNextAlive()
|
||||
end
|
||||
end
|
||||
|
||||
---@param skill TriggerSkill
|
||||
function GameLogic:addTriggerSkill(skill)
|
||||
if skill == nil or table.contains(self.skills, skill.name) then
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(self.skills, skill.name)
|
||||
|
||||
for _, event in ipairs(skill.refresh_events) do
|
||||
if self.refresh_skill_table[event] == nil then
|
||||
self.refresh_skill_table[event] = {}
|
||||
end
|
||||
table.insert(self.refresh_skill_table[event], skill)
|
||||
end
|
||||
|
||||
for _, event in ipairs(skill.events) do
|
||||
if self.skill_table[event] == nil then
|
||||
self.skill_table[event] = {}
|
||||
end
|
||||
table.insert(self.skill_table[event], skill)
|
||||
end
|
||||
|
||||
if skill.visible then
|
||||
if (Fk.related_skills[skill.name] == nil) then return end
|
||||
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
||||
if (s.class == TriggerSkill) then
|
||||
self:addTriggerSkill(s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param event Event
|
||||
---@param target ServerPlayer
|
||||
---@param data any
|
||||
function GameLogic:trigger(event, target, data)
|
||||
local room = self.room
|
||||
local broken = false
|
||||
local skills = self.skill_table[event] or {}
|
||||
local skills_to_refresh = self.refresh_skill_table[event] or {}
|
||||
local player = target
|
||||
|
||||
self.event_stack:push({event, target, data})
|
||||
|
||||
if target == nil then
|
||||
for _, skill in ipairs(skills_to_refresh) do
|
||||
if skill:canRefresh(event, target, player, data) then
|
||||
skill:refresh(event, target, player, data)
|
||||
end
|
||||
end
|
||||
|
||||
for _, skill in ipairs(skills) do
|
||||
if skill:triggerable(event, target, player, data) then
|
||||
broken = skill:trigger(event, target, player, data)
|
||||
if broken then break end
|
||||
end
|
||||
end
|
||||
|
||||
self.event_stack:pop()
|
||||
return broken
|
||||
end
|
||||
|
||||
repeat do
|
||||
-- refresh skills. This should not be broken
|
||||
for _, skill in ipairs(skills_to_refresh) do
|
||||
if skill:canRefresh(event, target, player, data) then
|
||||
skill:refresh(event, target, player, data)
|
||||
end
|
||||
end
|
||||
player = player.next
|
||||
end until player == target
|
||||
|
||||
---@param a TriggerSkill
|
||||
---@param b TriggerSkill
|
||||
local compare_func = function (a, b)
|
||||
return a.priority_table[event] > b.priority_table[event]
|
||||
end
|
||||
table.sort(skills, compare_func)
|
||||
|
||||
repeat do
|
||||
local triggerable_skills = {} ---@type table<number, TriggerSkill[]>
|
||||
local priority_table = {} ---@type number[]
|
||||
for _, skill in ipairs(skills) do
|
||||
if skill:triggerable(event, target, player, data) then
|
||||
local priority = skill.priority_table[event]
|
||||
if triggerable_skills[priority] == nil then
|
||||
triggerable_skills[priority] = {}
|
||||
end
|
||||
table.insert(triggerable_skills[priority], skill)
|
||||
if not table.contains(priority_table, priority) then
|
||||
table.insert(priority_table, priority)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, priority in ipairs(priority_table) do
|
||||
local triggerables = triggerable_skills[priority]
|
||||
local skill_names = {} ---@type string[]
|
||||
for _, skill in ipairs(triggerables) do
|
||||
table.insert(skill_names, skill.name)
|
||||
end
|
||||
|
||||
while #skill_names > 0 do
|
||||
local skill_name = room:askForChoice(player, skill_names)
|
||||
local skill = triggerables[table.indexOf(skill_names, skill_name)]
|
||||
broken = skill:trigger(event, target, player, data)
|
||||
if broken then break end
|
||||
table.removeOne(skill_names, skill_name)
|
||||
table.removeOne(triggerables, skill)
|
||||
end
|
||||
end
|
||||
|
||||
if broken then break end
|
||||
|
||||
player = player.next
|
||||
end until player == target
|
||||
|
||||
self.event_stack:pop()
|
||||
return broken
|
||||
end
|
||||
|
||||
return GameLogic
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
---@class Lobby : Object
|
||||
---@field lobby fk.Room
|
||||
Lobby = class("Lobby")
|
||||
|
||||
fk.lobby_callback = {}
|
||||
local db = fk.ServerInstance:getDatabase()
|
||||
|
||||
function Lobby:initialize(_lobby)
|
||||
self.lobby = _lobby
|
||||
self.lobby.callback = function(_self, command, jsonData)
|
||||
local cb = fk.lobby_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
print("Lobby error: Unknown command " .. command);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fk.lobby_callback["UpdateAvatar"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string newavatar ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, avatar = data[1], data[2]
|
||||
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
||||
Sql.exec(db, string.format(sql, avatar, id))
|
||||
local player = fk.ServerInstance:findPlayer(id)
|
||||
player:setAvatar(avatar)
|
||||
player:doNotify("UpdateAvatar", avatar)
|
||||
end
|
||||
|
||||
fk.lobby_callback["UpdatePassword"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string oldpassword, int newpassword ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, old, new = data[1], data[2], data[3]
|
||||
local sql_find = "SELECT password FROM userinfo WHERE id=%d;"
|
||||
local sql_update = "UPDATE userinfo SET password='%s' WHERE id=%d;"
|
||||
|
||||
local passed = false
|
||||
local result = Sql.exec_select(db, string.format(sql_find, id))
|
||||
passed = (result["password"][1] == sha256(old))
|
||||
if passed then
|
||||
Sql.exec(db, string.format(sql_update, sha256(new), id))
|
||||
end
|
||||
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(id))
|
||||
player:doNotify("UpdatePassword", passed and "1" or "0")
|
||||
end
|
||||
|
||||
fk.lobby_callback["CreateRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string name, int capacity ]
|
||||
local data = json.decode(jsonData)
|
||||
local owner = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local roomName = data[2]
|
||||
local capacity = data[3]
|
||||
fk.ServerInstance:createRoom(owner, roomName, capacity)
|
||||
end
|
||||
|
||||
fk.lobby_callback["EnterRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid, int roomId ]
|
||||
local data = json.decode(jsonData)
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local room = fk.ServerInstance:findRoom(tonumber(data[2]))
|
||||
room:addPlayer(player)
|
||||
end
|
||||
|
||||
function CreateRoom(_room)
|
||||
LobbyInstance = Lobby:new(_room)
|
||||
end
|
|
@ -1,17 +1,37 @@
|
|||
---@class Room : Object
|
||||
---@field room fk.Room
|
||||
---@field server Server
|
||||
---@field players table
|
||||
---@field alive_players table
|
||||
---@field players ServerPlayer[]
|
||||
---@field alive_players ServerPlayer[]
|
||||
---@field current ServerPlayer
|
||||
---@field game_finished boolean
|
||||
---@field timeout number
|
||||
---@field timeout integer
|
||||
local Room = class("Room")
|
||||
|
||||
-- load classes used by the game
|
||||
GameLogic = require "server.gamelogic"
|
||||
ServerPlayer = require "server.serverplayer"
|
||||
|
||||
fk.room_callback = {}
|
||||
|
||||
---@param _room fk.Room
|
||||
function Room:initialize(_room)
|
||||
self.room = _room
|
||||
self.server = nil
|
||||
self.players = {} -- ServerPlayer[]
|
||||
self.room.callback = function(_self, command, jsonData)
|
||||
local cb = fk.room_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
print("Lobby error: Unknown command " .. command);
|
||||
end
|
||||
end
|
||||
|
||||
self.room.startGame = function(_self)
|
||||
self:run()
|
||||
end
|
||||
|
||||
self.players = {}
|
||||
self.alive_players = {}
|
||||
self.current = nil
|
||||
self.game_finished = false
|
||||
self.timeout = _room:getTimeout()
|
||||
end
|
||||
|
@ -23,7 +43,6 @@ function Room:run()
|
|||
player.state = p:getStateString()
|
||||
player.room = self
|
||||
table.insert(self.players, player)
|
||||
self.server.players[player:getId()] = player
|
||||
end
|
||||
|
||||
self.logic = GameLogic:new(self)
|
||||
|
@ -189,4 +208,50 @@ function Room:gameOver()
|
|||
self.room:gameOver()
|
||||
end
|
||||
|
||||
return Room
|
||||
---@param id integer
|
||||
function Room:findPlayerById(id)
|
||||
for _, p in ipairs(self.players) do
|
||||
if p:getId() == id then
|
||||
return p
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param player ServerPlayer
|
||||
---@param choices string[]
|
||||
function Room:askForChoice(player, choices)
|
||||
return choices[1]
|
||||
end
|
||||
|
||||
fk.room_callback["QuitRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid ]
|
||||
local data = json.decode(jsonData)
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local room = player:getRoom()
|
||||
if not room:isLobby() then
|
||||
room:removePlayer(player)
|
||||
end
|
||||
end
|
||||
|
||||
fk.room_callback["PlayerStateChanged"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string stateString ]
|
||||
-- note: this function is not called by Router.
|
||||
-- note: when this function is called, the room must be started
|
||||
local data = json.decode(jsonData)
|
||||
local id = data[1]
|
||||
local stateString = data[2]
|
||||
RoomInstance:findPlayerById(id).state = stateString
|
||||
end
|
||||
|
||||
fk.room_callback["DoLuaScript"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string luaScript ]
|
||||
-- warning: only use this in debugging mode.
|
||||
if not DebugMode then return end
|
||||
local data = json.decode(jsonData)
|
||||
assert(load(data[2]))()
|
||||
end
|
||||
|
||||
function CreateRoom(_room)
|
||||
RoomInstance = Room:new(_room)
|
||||
end
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
---@class Server : Object
|
||||
---@field server fk.Server
|
||||
---@field db fk.SQLite3
|
||||
---@field rooms table
|
||||
---@field players table
|
||||
Server = class('Server')
|
||||
|
||||
-- load server classes
|
||||
Room = require "server.room"
|
||||
GameLogic = require "server.gamelogic"
|
||||
ServerPlayer = require "server.serverplayer"
|
||||
|
||||
fk.server_callback = {}
|
||||
|
||||
function Server:initialize()
|
||||
self.server = fk.ServerInstance
|
||||
self.db = fk.ServerInstance:getDatabase()
|
||||
self.server.callback = function(_self, command, jsonData)
|
||||
local cb = fk.server_callback[command]
|
||||
if (type(cb) == "function") then
|
||||
cb(jsonData)
|
||||
else
|
||||
print("Server error: Unknown command " .. command);
|
||||
end
|
||||
end
|
||||
|
||||
self.server.startRoom = function(_self, _room)
|
||||
local room = Room:new(_room)
|
||||
room.server = self
|
||||
table.insert(self.rooms, room)
|
||||
|
||||
room:run()
|
||||
|
||||
-- If room.run returns, the game is over and lua room
|
||||
-- should be destoried now.
|
||||
-- This behavior does not affect C++ Room.
|
||||
table.removeOne(self.rooms, room)
|
||||
end
|
||||
|
||||
self.rooms = {} -- id --> Room(Started)
|
||||
self.players = {} -- id --> ServerPlayer
|
||||
end
|
||||
|
||||
fk.server_callback["UpdateAvatar"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string newavatar ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, avatar = data[1], data[2]
|
||||
local sql = "UPDATE userinfo SET avatar='%s' WHERE id=%d;"
|
||||
Sql.exec(ServerInstance.db, string.format(sql, avatar, id))
|
||||
local player = fk.ServerInstance:findPlayer(id)
|
||||
player:setAvatar(avatar)
|
||||
player:doNotify("UpdateAvatar", avatar)
|
||||
end
|
||||
|
||||
fk.server_callback["UpdatePassword"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string oldpassword, int newpassword ]
|
||||
local data = json.decode(jsonData)
|
||||
local id, old, new = data[1], data[2], data[3]
|
||||
local sql_find = "SELECT password FROM userinfo WHERE id=%d;"
|
||||
local sql_update = "UPDATE userinfo SET password='%s' WHERE id=%d;"
|
||||
|
||||
local db = ServerInstance.db
|
||||
local passed = false
|
||||
local result = Sql.exec_select(db, string.format(sql_find, id))
|
||||
passed = (result["password"][1] == sha256(old))
|
||||
if passed then
|
||||
Sql.exec(db, string.format(sql_update, sha256(new), id))
|
||||
end
|
||||
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(id))
|
||||
player:doNotify("UpdatePassword", passed and "1" or "0")
|
||||
end
|
||||
|
||||
fk.server_callback["CreateRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string name, int capacity ]
|
||||
local data = json.decode(jsonData)
|
||||
local owner = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local roomName = data[2]
|
||||
local capacity = data[3]
|
||||
fk.ServerInstance:createRoom(owner, roomName, capacity)
|
||||
end
|
||||
|
||||
fk.server_callback["EnterRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid, int roomId ]
|
||||
local data = json.decode(jsonData)
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local room = fk.ServerInstance:findRoom(tonumber(data[2]))
|
||||
room:addPlayer(player)
|
||||
end
|
||||
|
||||
fk.server_callback["QuitRoom"] = function(jsonData)
|
||||
-- jsonData: [ int uid ]
|
||||
local data = json.decode(jsonData)
|
||||
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
|
||||
local room = player:getRoom()
|
||||
if not room:isLobby() then
|
||||
room:removePlayer(player)
|
||||
end
|
||||
end
|
||||
|
||||
fk.server_callback["DoLuaScript"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string luaScript ]
|
||||
-- warning: only use this in debugging mode.
|
||||
if not DebugMode then return end
|
||||
local data = json.decode(jsonData)
|
||||
assert(load(data[2]))()
|
||||
end
|
||||
|
||||
fk.server_callback["PlayerStateChanged"] = function(jsonData)
|
||||
-- jsonData: [ int uid, string stateString ]
|
||||
-- note: this function is not called by Router.
|
||||
local data = json.decode(jsonData)
|
||||
local id = data[1]
|
||||
local stateString = data[2]
|
||||
ServerInstance.players[id].state = stateString
|
||||
end
|
||||
|
||||
ServerInstance = Server:new()
|
|
@ -22,7 +22,7 @@ function ServerPlayer:initialize(_self)
|
|||
self.reply_ready = false
|
||||
end
|
||||
|
||||
---@return number
|
||||
---@return integer
|
||||
function ServerPlayer:getId()
|
||||
return self.serverplayer:getId()
|
||||
end
|
||||
|
@ -38,7 +38,7 @@ end
|
|||
--- *timeout* must not be negative. If nil, room.timeout is used.
|
||||
---@param command string
|
||||
---@param jsonData string
|
||||
---@param timeout number
|
||||
---@param timeout integer
|
||||
function ServerPlayer:doRequest(command, jsonData, timeout)
|
||||
timeout = timeout or self.room.timeout
|
||||
self.client_reply = ""
|
||||
|
@ -49,7 +49,7 @@ end
|
|||
--- Wait for at most *timeout* seconds for reply from client.
|
||||
---
|
||||
--- If *timeout* is negative or **nil**, the function will wait forever until get reply.
|
||||
---@param timeout number # seconds to wait
|
||||
---@param timeout integer # seconds to wait
|
||||
---@return string reply # JSON data
|
||||
function ServerPlayer:waitForReply(timeout)
|
||||
local result = ""
|
||||
|
@ -64,4 +64,25 @@ function ServerPlayer:waitForReply(timeout)
|
|||
return result
|
||||
end
|
||||
|
||||
---@param skill Skill
|
||||
function ServerPlayer:hasSkill(skill)
|
||||
return table.contains(self.player_skills, skill)
|
||||
end
|
||||
|
||||
function ServerPlayer:isAlive()
|
||||
return self.dead == false
|
||||
end
|
||||
|
||||
function ServerPlayer:getNextAlive()
|
||||
if #self.room.alive_players == 0 then
|
||||
return self
|
||||
end
|
||||
|
||||
local ret = self.next
|
||||
while ret.dead do
|
||||
ret = ret.next
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
return ServerPlayer
|
||||
|
|
|
@ -11,7 +11,7 @@ fk = {}
|
|||
SPlayerList = {}
|
||||
|
||||
--- * get microsecond from Epoch
|
||||
---@return number microsecond
|
||||
---@return integer microsecond
|
||||
function fk:GetMicroSecond()end
|
||||
|
||||
--- construct a QList<ServerPlayer *>.
|
||||
|
|
|
@ -5,13 +5,22 @@
|
|||
--- middleclass
|
||||
class = {}
|
||||
|
||||
---@param class class
|
||||
---@return boolean
|
||||
function class:isSubclassOf(class) end
|
||||
|
||||
---@class Object
|
||||
---@field class class
|
||||
Object = {}
|
||||
|
||||
---@generic T
|
||||
---@param self T
|
||||
function Object:initialize(...) end
|
||||
|
||||
function Object.new(...)end
|
||||
---@generic T
|
||||
---@param self T
|
||||
---@return T
|
||||
function Object:new(...)end
|
||||
|
||||
---@param name string
|
||||
function Object:subclass(name)end
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
---@class fk.Player
|
||||
FPlayer = {}
|
||||
|
||||
---@return number id
|
||||
---@return integer id
|
||||
function FPlayer:getId()end
|
||||
|
||||
---@param id number
|
||||
---@param id integer
|
||||
function FPlayer:setId(id)end
|
||||
|
||||
---@return string name
|
||||
|
@ -47,13 +47,13 @@ function FServerPlayer:speak(msg)end
|
|||
--- *timeout* must not be negative or **nil**.
|
||||
---@param command string
|
||||
---@param jsonData string
|
||||
---@param timeout number
|
||||
---@param timeout integer
|
||||
function FServerPlayer:doRequest(command,jsonData,timeout)end
|
||||
|
||||
--- Wait for at most *timeout* seconds for reply from client.
|
||||
---
|
||||
--- If *timeout* is negative or **nil**, the function will wait forever until get reply.
|
||||
---@param timeout number # seconds to wait
|
||||
---@param timeout integer # seconds to wait
|
||||
---@return string reply # JSON data
|
||||
---@overload fun()
|
||||
function FServerPlayer:waitForReply(timeout)end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---@meta
|
||||
|
||||
---@return number length
|
||||
---@return integer length
|
||||
function SPlayerList:length()end
|
||||
|
||||
---@param e fk.ServerPlayer
|
||||
|
@ -10,7 +10,7 @@ function SPlayerList:append(e)end
|
|||
---@return boolean
|
||||
function SPlayerList:contains(e)end
|
||||
|
||||
---@param index number
|
||||
---@param index integer
|
||||
---@return fk.ServerPlayer | nil
|
||||
function SPlayerList:at(index)end
|
||||
|
||||
|
|
|
@ -12,14 +12,17 @@ FRoom = {}
|
|||
|
||||
---@param owner fk.ServerPlayer
|
||||
---@param name string
|
||||
---@param capacity number
|
||||
---@param capacity integer
|
||||
function FServer:createRoom(owner,name,capacity)end
|
||||
|
||||
---@param id number
|
||||
---@param id integer
|
||||
---@return fk.Room room
|
||||
function FServer:findRoom(id)end
|
||||
|
||||
---@param id number
|
||||
---@return fk.Room room
|
||||
function FServer:lobby()end
|
||||
|
||||
---@param id integer
|
||||
---@return fk.ServerPlayer player
|
||||
function FServer:findPlayer(id)end
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
GameRule = fk.CreateTriggerSkill{
|
||||
name = "game_rule",
|
||||
events = {
|
||||
fk.GameStart, fk.TurnStart,
|
||||
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
||||
},
|
||||
priority = 0,
|
||||
|
||||
can_trigger = function(self, event, target, player, data)
|
||||
return (target == player) or (target == nil)
|
||||
end,
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
local extension = Package:new("standard")
|
||||
extension.metadata = require "packages.standard.metadata"
|
||||
dofile "packages/standard/game_rule.lua"
|
||||
|
||||
Fk:loadTranslationTable{
|
||||
["wei"] = "魏",
|
||||
|
|
|
@ -8,6 +8,7 @@ callbacks["NetworkDelayTest"] = function(jsonData) {
|
|||
}
|
||||
|
||||
callbacks["ErrorMsg"] = function(jsonData) {
|
||||
console.log("ERROR: " + jsonData);
|
||||
toast.show(jsonData, 5000);
|
||||
mainWindow.busy = false;
|
||||
}
|
||||
|
|
|
@ -21,14 +21,43 @@ bool DoLuaScript(lua_State *L, const char *script)
|
|||
|
||||
luaL_loadfile(L, script);
|
||||
int error = lua_pcall(L, 0, LUA_MULTRET, -2);
|
||||
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// For Lua debugging
|
||||
void Dumpstack(lua_State *L)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
for (int i = 1; i <= top; i++) {
|
||||
printf("%d\t%s\t", i, luaL_typename(L, i));
|
||||
switch (lua_type(L, i)) {
|
||||
case LUA_TNUMBER:
|
||||
printf("%g\n",lua_tonumber(L, i));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
printf("%s\n",lua_tostring(L, i));
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
printf("%s\n", (lua_toboolean(L, i) ? "true" : "false"));
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
printf("%s\n", "nil");
|
||||
break;
|
||||
default:
|
||||
printf("%p\n",lua_topointer(L, i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3 *OpenDatabase(const QString &filename)
|
||||
{
|
||||
sqlite3 *ret;
|
||||
|
|
|
@ -155,12 +155,11 @@ void Router::handlePacket(const QByteArray& rawPacket)
|
|||
if (type & DEST_CLIENT) {
|
||||
ClientInstance->callLua(command, jsonData);
|
||||
} else {
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
|
||||
// Add the uid of sender to jsonData
|
||||
QJsonArray arr = QJsonDocument::fromJson(jsonData.toUtf8()).array();
|
||||
arr.prepend(
|
||||
qobject_cast<ServerPlayer *>(parent())->getId()
|
||||
);
|
||||
ServerInstance->callLua(command, QJsonDocument(arr).toJson());
|
||||
arr.prepend(player->getId());
|
||||
player->getRoom()->callLua(command, QJsonDocument(arr).toJson());
|
||||
}
|
||||
}
|
||||
else if (type & TYPE_REQUEST) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "room.h"
|
||||
#include "serverplayer.h"
|
||||
#include "server.h"
|
||||
#include "util.h"
|
||||
|
||||
Room::Room(Server* server)
|
||||
{
|
||||
|
@ -15,12 +16,22 @@ Room::Room(Server* server)
|
|||
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
|
||||
connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer);
|
||||
}
|
||||
|
||||
L = CreateLuaState();
|
||||
DoLuaScript(L, "lua/freekill.lua");
|
||||
if (isLobby()) {
|
||||
DoLuaScript(L, "lua/server/lobby.lua");
|
||||
} else {
|
||||
DoLuaScript(L, "lua/server/room.lua");
|
||||
}
|
||||
initLua();
|
||||
}
|
||||
|
||||
Room::~Room()
|
||||
{
|
||||
// TODO
|
||||
disconnect();
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
Server *Room::getServer() const
|
||||
|
@ -83,7 +94,15 @@ void Room::setOwner(ServerPlayer *owner)
|
|||
|
||||
void Room::addPlayer(ServerPlayer *player)
|
||||
{
|
||||
if (isFull() || !player) return;
|
||||
if (!player) return;
|
||||
|
||||
if (isFull() || gameStarted) {
|
||||
player->doNotify("ErrorMsg", "Room is full or already started!");
|
||||
if (runned_players.contains(player->getId())) {
|
||||
player->doNotify("ErrorMsg", "Running away is shameful.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray jsonData;
|
||||
|
||||
|
@ -120,7 +139,7 @@ void Room::addPlayer(ServerPlayer *player)
|
|||
player->doNotify("RoomOwner", QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
|
||||
if (isFull())
|
||||
if (isFull() && !gameStarted)
|
||||
start();
|
||||
}
|
||||
emit playerAdded(player);
|
||||
|
@ -133,10 +152,18 @@ void Room::removePlayer(ServerPlayer *player)
|
|||
|
||||
if (isLobby()) return;
|
||||
|
||||
// player->doNotify("QuitRoom", "[]");
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
||||
if (gameStarted) {
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
doBroadcastNotify(getPlayers(), "PlayerRunned", QJsonDocument(jsonData).toJson());
|
||||
runned_players << player->getId();
|
||||
|
||||
// TODO: create a robot for runned player.
|
||||
} else {
|
||||
QJsonArray jsonData;
|
||||
jsonData << player->getId();
|
||||
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
|
||||
}
|
||||
|
||||
if (isAbandoned()) {
|
||||
emit abandoned();
|
||||
|
@ -198,6 +225,7 @@ void Room::doBroadcastNotify(const QList<ServerPlayer *> targets,
|
|||
void Room::gameOver()
|
||||
{
|
||||
gameStarted = false;
|
||||
runned_players.clear();
|
||||
// clean offline players
|
||||
foreach (ServerPlayer *p, players) {
|
||||
if (p->getState() == Player::Offline) {
|
||||
|
@ -209,6 +237,5 @@ void Room::gameOver()
|
|||
void Room::run()
|
||||
{
|
||||
gameStarted = true;
|
||||
getServer()->roomStart(this);
|
||||
roomStart();
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,15 @@ public:
|
|||
|
||||
void gameOver();
|
||||
|
||||
lua_State *getLuaState() const;
|
||||
|
||||
void initLua();
|
||||
void callLua(const QString &command, const QString &jsonData);
|
||||
LuaFunction callback;
|
||||
|
||||
void roomStart();
|
||||
LuaFunction startGame;
|
||||
|
||||
signals:
|
||||
void abandoned();
|
||||
|
||||
|
@ -66,9 +75,12 @@ private:
|
|||
|
||||
ServerPlayer *owner; // who created this room?
|
||||
QList<ServerPlayer *> players;
|
||||
QList<int> runned_players;
|
||||
bool gameStarted;
|
||||
|
||||
int timeout;
|
||||
|
||||
lua_State *L;
|
||||
};
|
||||
|
||||
#endif // _ROOM_H
|
||||
|
|
|
@ -12,6 +12,7 @@ Server::Server(QObject* parent)
|
|||
: QObject(parent)
|
||||
{
|
||||
ServerInstance = this;
|
||||
db = OpenDatabase();
|
||||
server = new ServerSocket();
|
||||
server->setParent(this);
|
||||
connect(server, &ServerSocket::new_connection,
|
||||
|
@ -22,19 +23,12 @@ Server::Server(QObject* parent)
|
|||
createRoom(nullptr, "Lobby", INT32_MAX);
|
||||
connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList);
|
||||
connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList);
|
||||
|
||||
db = OpenDatabase();
|
||||
|
||||
L = CreateLuaState();
|
||||
DoLuaScript(L, "lua/freekill.lua");
|
||||
DoLuaScript(L, "lua/server/server.lua");
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
ServerInstance = nullptr;
|
||||
m_lobby->deleteLater();
|
||||
lua_close(L);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
|
@ -257,9 +251,9 @@ void Server::onUserDisconnected()
|
|||
|
||||
void Server::onUserStateChanged()
|
||||
{
|
||||
Player *player = qobject_cast<Player *>(sender());
|
||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||
QJsonArray arr;
|
||||
arr << player->getId();
|
||||
arr << player->getStateString();
|
||||
callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
|
||||
player->getRoom()->callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
|
||||
}
|
||||
|
|
|
@ -27,12 +27,6 @@ public:
|
|||
|
||||
sqlite3 *getDatabase();
|
||||
|
||||
void callLua(const QString &command, const QString &jsonData);
|
||||
LuaFunction callback;
|
||||
|
||||
void roomStart(Room *room);
|
||||
LuaFunction startRoom;
|
||||
|
||||
signals:
|
||||
void roomCreated(Room *room);
|
||||
void playerAdded(ServerPlayer *player);
|
||||
|
@ -55,7 +49,6 @@ private:
|
|||
QHash<int, ServerPlayer *> players;
|
||||
|
||||
sqlite3 *db;
|
||||
lua_State *L;
|
||||
|
||||
void handleNameAndPassword(ClientSocket *client, const QString &name, const QString &password);
|
||||
};
|
||||
|
|
|
@ -43,9 +43,12 @@ void Client::callLua(const QString& command, const QString& json_data)
|
|||
lua_pushstring(L, json_data.toUtf8());
|
||||
|
||||
int error = lua_pcall(L, 3, 0, -5);
|
||||
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
%}
|
||||
|
|
|
@ -2,57 +2,14 @@
|
|||
%nodefaultdtor Server;
|
||||
class Server : public QObject {
|
||||
public:
|
||||
Room *lobby() const;
|
||||
void createRoom(ServerPlayer *owner, const QString &name, int capacity);
|
||||
Room *findRoom(int id) const;
|
||||
ServerPlayer *findPlayer(int id) const;
|
||||
|
||||
sqlite3 *getDatabase();
|
||||
|
||||
LuaFunction callback;
|
||||
LuaFunction startRoom;
|
||||
};
|
||||
|
||||
%{
|
||||
void Server::callLua(const QString& command, const QString& json_data)
|
||||
{
|
||||
Q_ASSERT(callback);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Server, 0);
|
||||
lua_pushstring(L, command.toUtf8());
|
||||
lua_pushstring(L, json_data.toUtf8());
|
||||
|
||||
int error = lua_pcall(L, 3, 0, -5);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::roomStart(Room *room) {
|
||||
Q_ASSERT(startRoom);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, startRoom);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Server, 0);
|
||||
SWIG_NewPointerObj(L, room, SWIGTYPE_p_Room, 0);
|
||||
|
||||
int error = lua_pcall(L, 2, 0, -4);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
}
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
extern Server *ServerInstance;
|
||||
|
||||
%nodefaultctor Room;
|
||||
|
@ -93,5 +50,69 @@ public:
|
|||
);
|
||||
|
||||
void gameOver();
|
||||
|
||||
LuaFunction callback;
|
||||
LuaFunction startGame;
|
||||
};
|
||||
|
||||
%{
|
||||
void Room::initLua()
|
||||
{
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
lua_getglobal(L, "CreateRoom");
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
int error = lua_pcall(L, 1, 0, -2);
|
||||
lua_pop(L, 1);
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
}
|
||||
}
|
||||
|
||||
void Room::callLua(const QString& command, const QString& json_data)
|
||||
{
|
||||
Q_ASSERT(callback);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
lua_pushstring(L, command.toUtf8());
|
||||
lua_pushstring(L, json_data.toUtf8());
|
||||
|
||||
int error = lua_pcall(L, 3, 0, -5);
|
||||
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void Room::roomStart() {
|
||||
Q_ASSERT(startGame);
|
||||
|
||||
lua_getglobal(L, "debug");
|
||||
lua_getfield(L, -1, "traceback");
|
||||
lua_replace(L, -2);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, startGame);
|
||||
SWIG_NewPointerObj(L, this, SWIGTYPE_p_Room, 0);
|
||||
|
||||
int error = lua_pcall(L, 1, 0, -3);
|
||||
|
||||
if (error) {
|
||||
const char *error_msg = lua_tostring(L, -1);
|
||||
qDebug() << error_msg;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
|
Loading…
Reference in New Issue