diff --git a/.ava-x2l-config.json b/.ava-x2l-config.json index 78ee6f6..d0a1999 100644 --- a/.ava-x2l-config.json +++ b/.ava-x2l-config.json @@ -1,9 +1,7 @@ { - "input_folder": "./xls", - "output_folder": "./code", + "input_folder": "./Xls", + "output_folder": "./Code", "output_lua_template": "['World']['Global']['Xls']['{sheet_name}XlsModule'].ModuleScript.lua", - "kv_xls": [ - "GlobalSetting.xlsx" - ], + "kv_xls": "['GlobalSetting.xlsx']", "translate_xls": "LanguagePack.xls" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 92d5dad..84eb89b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ code/*.xlsx *.smap.storage *.iso ~$* -*.idea/ \ No newline at end of file +*.idea/ +*.vscode/ \ No newline at end of file diff --git a/code/['Archetypes']['GuideArchetype']['ClickGuide']['GuideEffectScript'].Script.lua b/Code/['Archetypes']['GuideArchetype']['ClickGuide']['GuideEffectScript'].Script.lua similarity index 100% rename from code/['Archetypes']['GuideArchetype']['ClickGuide']['GuideEffectScript'].Script.lua rename to Code/['Archetypes']['GuideArchetype']['ClickGuide']['GuideEffectScript'].Script.lua diff --git a/code/['Archetypes']['Player']['Local']['C_Code']['ClientMainScript'].Script.lua b/Code/['Archetypes']['Player']['Local']['C_Code']['ClientMainScript'].Script.lua similarity index 100% rename from code/['Archetypes']['Player']['Local']['C_Code']['ClientMainScript'].Script.lua rename to Code/['Archetypes']['Player']['Local']['C_Code']['ClientMainScript'].Script.lua diff --git a/Code/['World']['Global']['AutoAssignTeamScript'].Script.lua b/Code/['World']['Global']['AutoAssignTeamScript'].Script.lua new file mode 100644 index 0000000..98c605f --- /dev/null +++ b/Code/['World']['Global']['AutoAssignTeamScript'].Script.lua @@ -0,0 +1,8 @@ +--- 玩家加入 +-- @script Auto assign +-- @copyright Lilith Games, Avatar Team + +--- 编辑器默认方法, 删了报错 +-- run once when script init +autoAssign = function() +end diff --git a/code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua rename to Code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua similarity index 96% rename from code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua rename to Code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua index 2071385..c12cba4 100644 --- a/code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua @@ -6,7 +6,7 @@ local Const = {} -- e.g. (need DELETE) Const.MAX_PLAYERS = 4 ---语言枚举 +-- 语言枚举 Const.LanguageEnum = { CHS = 'CHS', -- 简体中文 CHT = 'CHT', -- 繁体中文 diff --git a/Code/['World']['Global']['Define']['DefaultModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['DefaultModule'].ModuleScript.lua new file mode 100644 index 0000000..3595d73 --- /dev/null +++ b/Code/['World']['Global']['Define']['DefaultModule'].ModuleScript.lua @@ -0,0 +1,27 @@ +--- 全局默认定义:用于定义数据,节点属性等 +--- @module Data Default +--- @copyright Lilith Games, Avatar Team +local Default = {} + +--! 说明:这个module当作脚本使用 + +--* Data.Global和Data.Player中的默认值,用于框架初始化 + +Data.Default = Data.Default or {} + +-- 全局变量定义 +Data.Default.Global = {} + +-- 玩家数据,初始化定义 +Data.Default.Player = { + -- 玩家ID, 框架默认 + uid = '', + -- 玩家属性 + attr = {}, + -- 背包 + bag = {}, + -- 统计数据 + stats = {} +} + +return Default diff --git a/code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua rename to Code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Framework']['ClientBaseModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientBaseModule'].ModuleScript.lua similarity index 64% rename from code/['World']['Global']['Framework']['ClientBaseModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['Client']['ClientBaseModule'].ModuleScript.lua index 1deb376..32f20be 100644 --- a/code/['World']['Global']['Framework']['ClientBaseModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['Client']['ClientBaseModule'].ModuleScript.lua @@ -15,4 +15,13 @@ function ClientBase:InitDefault(_module) EventUtil.LinkConnects(localPlayer.C_Event, _module, self) end +--- Debug模式下打印日志 +-- self.debug 针对模块本身的debug开关 +-- FrameworkConfig.DebugMode 框架中的全局debug开关 +function ClientBase:Log(...) + if self.debug and FrameworkConfig.DebugMode then + print(string.format('[%s]', self.name), ...) + end +end + return ClientBase diff --git a/Code/['World']['Global']['Framework']['Client']['ClientDataSyncModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientDataSyncModule'].ModuleScript.lua new file mode 100644 index 0000000..22b413b --- /dev/null +++ b/Code/['World']['Global']['Framework']['Client']['ClientDataSyncModule'].ModuleScript.lua @@ -0,0 +1,101 @@ +--- 游戏客户端数据同步 +--- @module Client Sync Data, Client-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local ClientDataSync = {} + +-- Localize global vars +local FrameworkConfig, MetaData = FrameworkConfig, MetaData + +-- 客户端私有数据 +local rawDataGlobal = {} +local rawDataPlayer = {} + +--- 打印数据同步日志 +local PrintLog = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowDataSyncLog and function(...) + print('[DataSync][Client]', ...) + end or function() + end + +--! 初始化 + +--- 数据初始化 +function ClientDataSync.Init() + print('[DataSync][Client] Init()') + InitEventsAndListeners() + InitDataDefines() +end + +--- 初始化事件和绑定Handler +function InitEventsAndListeners() + if localPlayer.C_Event == nil then + world:CreateObject('FolderObject', 'S_Event', localPlayer) + end + + -- 数据同步事件 + world:CreateObject('CustomEvent', 'DataSyncS2CEvent', localPlayer.C_Event) + localPlayer.C_Event.DataSyncS2CEvent:Connect(DataSyncS2CEventHandler) + + -- 长期存储成功事件 + if not localPlayer.C_Event.LoadPlayerDataSuccessEvent then + world:CreateObject('CustomEvent', 'LoadPlayerDataSuccessEvent', localPlayer.C_Event) + end +end + +--- 校验数据定义 +function InitDataDefines() + --* 客户端全局数据 + Data.Global = Data.Global or MetaData.New(rawDataGlobal, MetaData.Enum.GLOBAL, MetaData.Enum.CLIENT) + -- 默认赋值 + for k, v in pairs(Data.Default.Global) do + Data.Global[k] = v + end + + --* 客户端玩家数据 + local uid = localPlayer.UserId + local path = MetaData.Enum.PLAYER .. uid + Data.Player = Data.Player or MetaData.New(rawDataPlayer, path, uid) + -- 默认赋值 + for k, v in pairs(Data.Default.Player) do + Data.Player[k] = v + end +end + +--- 开始同步 +function ClientDataSync.Start() + print('[DataSync][Client] 客户端数据同步开启') + MetaData.ClientSync = true +end + +--! Event handler + +--- 数据同步事件Handler +function DataSyncS2CEventHandler(_path, _value) + if not MetaData.ClientSync then + return + end + + PrintLog(string.format('收到 _path = %s, _value = %s', _path, table.dump(_value))) + + local uid = localPlayer.UserId + + --* 收到服务器数据 + if string.startswith(_path, MetaData.Enum.GLOBAL) then + --* Data.Global 全局数据 + MetaData.Set(rawDataGlobal, _path, _value, uid, false) + elseif string.startswith(_path, MetaData.Enum.PLAYER .. uid) then + --* Data.Player 玩家数据 + MetaData.Set(rawDataPlayer, _path, _value, uid, false) + else + error( + string.format( + '[DataSync][Client] _path错误 _player = %s, _path = %s, _value = %s', + localPlayer, + _path, + table.dump(_value) + ) + ) + end +end + +return ClientDataSync diff --git a/code/['World']['Global']['Framework']['ClientHeartbeatModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientHeartbeatModule'].ModuleScript.lua similarity index 80% rename from code/['World']['Global']['Framework']['ClientHeartbeatModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['Client']['ClientHeartbeatModule'].ModuleScript.lua index 99f36a4..dcc685c 100644 --- a/code/['World']['Global']['Framework']['ClientHeartbeatModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['Client']['ClientHeartbeatModule'].ModuleScript.lua @@ -5,17 +5,17 @@ local ClientHeartbeat = {} -- Localize global vars -local Setting = FrameworkConfig.Client +local FrameworkConfig = FrameworkConfig -- 心跳包间隔时间,单位:秒 -local HEARTBEAT_DELTA = Setting.HeartbeatDelta +local HEARTBEAT_DELTA = FrameworkConfig.Client.HeartbeatDelta -- 心跳阈值,单位:秒,范围定义如下: -- 0s -> threshold_1 : connected -- threshold_1 -> threshold_2 : disconnected, weak network -- threshold_2 -> longer : disconnected, quit server -local HEARTBEAT_THRESHOLD_1 = Setting.HeartbeatThreshold1 * 1000 -- second => ms -local HEARTBEAT_THRESHOLD_2 = Setting.HeartbeatThreshold2 * 1000 -- second => ms +local HEARTBEAT_THRESHOLD_1 = FrameworkConfig.Client.HeartbeatThreshold1 * 1000 -- second => ms +local HEARTBEAT_THRESHOLD_2 = FrameworkConfig.Client.HeartbeatThreshold2 * 1000 -- second => ms -- 玩家心跳连接状态 local HeartbeatEnum = { @@ -37,7 +37,7 @@ local diff -- 时间戳插值 local sTmpTs, cTmpTs -- 时间戳缓存 --- 打印心跳日志 -local PrintHb = Setting.ShowHeartbeatLog and function(...) +local PrintHb = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowHeartbeatLog and function(...) print('[Heartbeat][Client]', ...) end or function() end @@ -127,13 +127,13 @@ end function CheckPlayerJoin(_player, _sTimestamp) if not cache.sTimestamp then --* 玩家新加入 OnPlayerJoinEvent - print('[Heartbeat][Client] OnPlayerJoinEvent, 新玩家加入,', localPlayer) - NetUtil.Fire_C('OnPlayerJoinEvent', localPlayer) + print('[Heartbeat][Client] OnPlayerJoinEvent, 新玩家加入,', localPlayer, localPlayer.UserId) + NetUtil.Fire_C('OnPlayerJoinEvent', localPlayer, localPlayer.UserId) cache.state = HeartbeatEnum.CONNECT elseif cache.state == HeartbeatEnum.DISCONNECT then --* 玩家断线重连 OnPlayerReconnectEvent - print('[Heartbeat][Client] OnPlayerReconnectEvent, 玩家断线重连,', localPlayer) - NetUtil.Fire_C('OnPlayerReconnectEvent', localPlayer) + print('[Heartbeat][Client] OnPlayerReconnectEvent, 玩家断线重连,', localPlayer, localPlayer.UserId) + NetUtil.Fire_C('OnPlayerReconnectEvent', localPlayer, localPlayer.UserId) cache.state = HeartbeatEnum.CONNECT end end @@ -145,21 +145,24 @@ function CheckPlayerState(_player, _cTimestamp) end diff = _cTimestamp - cache.cTimestamp PrintHb(string.format('==========================================> diff = %s, %s', diff * .001, localPlayer)) - if cache.state == HeartbeatEnum.CONNECT and diff > HEARTBEAT_THRESHOLD_1 then + if diff < HEARTBEAT_THRESHOLD_1 then + --* 玩家在线 + cache.state = HeartbeatEnum.CONNECT + elseif cache.state == HeartbeatEnum.CONNECT and diff >= HEARTBEAT_THRESHOLD_1 then --* 玩家断线,弱网环境 print('[Heartbeat][Client] OnPlayerDisconnectEvent, 玩家离线, 弱网环境,', localPlayer) - NetUtil.Fire_C('OnPlayerDisconnectEvent', localPlayer) + NetUtil.Fire_C('OnPlayerDisconnectEvent', localPlayer, localPlayer.UserId) cache.state = HeartbeatEnum.DISCONNECT - elseif cache.state == HeartbeatEnum.DISCONNECT and diff > HEARTBEAT_THRESHOLD_2 then + elseif cache.state == HeartbeatEnum.DISCONNECT and diff >= HEARTBEAT_THRESHOLD_2 then --* 玩家断线, 退出游戏 -- QuitGame() - NetUtil.Fire_C('OnPlayerLeaveEvent', localPlayer) + NetUtil.Fire_C('OnPlayerLeaveEvent', localPlayer, localPlayer.UserId) end end --- 退出游戏 function QuitGame() - print('[Heartbeat][Client] Game.Quit(), 玩家退出游戏') + print('[Heartbeat][Client] Game.Quit(), 玩家退出游戏', localPlayer, localPlayer.UserId) Game.Quit() end diff --git a/code/['World']['Global']['Framework']['ClientModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientModule'].ModuleScript.lua similarity index 90% rename from code/['World']['Global']['Framework']['ClientModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['Client']['ClientModule'].ModuleScript.lua index fc59abd..469bba8 100644 --- a/code/['World']['Global']['Framework']['ClientModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['Client']['ClientModule'].ModuleScript.lua @@ -36,8 +36,8 @@ function InitClient() print('[Client] InitClient()') InitRandomSeed() InitHeartbeat() + InitDataSync() InitClientCustomEvents() - PreloadCsv() GenInitAndUpdateList() RunInitDefault() InitOtherModules() @@ -50,6 +50,12 @@ function InitHeartbeat() ClientHeartbeat.Init() end +--- 初始化数据同步 +function InitDataSync() + assert(ClientDataSync, '[Server][DataSync] 找不到ClientDataSync,请联系张远程') + ClientDataSync.Init() +end + --- 初始化客户端的CustomEvent function InitClientCustomEvents() if localPlayer.C_Event == nil then @@ -77,9 +83,15 @@ end --- 生成需要Init和Update的模块列表 function GenInitAndUpdateList() + -- TODO: 改成在FrameworkConfig中配置 + -- Init Default ModuleUtil.GetModuleListWithFunc(Module.C_Module, 'InitDefault', initDefaultList) + -- Init + ModuleUtil.GetModuleListWithFunc(Define, 'Init', initList) ModuleUtil.GetModuleListWithFunc(Module.C_Module, 'Init', initList) + -- Update ModuleUtil.GetModuleListWithFunc(Module.C_Module, 'Update', updateList) + -- Plugin for _, m in pairs(Config.PluginModules) do ModuleUtil.GetModuleListWithFunc(m, 'InitDefault', initDefaultList) ModuleUtil.GetModuleListWithFunc(m, 'Init', initList) @@ -99,14 +111,6 @@ function InitRandomSeed() math.randomseed(os.time()) end ---- 预加载所有的CSV表格 -function PreloadCsv() - print('[Client] PreloadCsv()') - if Config.ClientPreload and #Config.ClientPreload > 0 then - CsvUtil.PreloadCsv(Config.ClientPreload, Csv, Config) - end -end - --- 初始化包含Init()方法的模块 function InitOtherModules() for _, m in ipairs(initList) do @@ -126,6 +130,9 @@ function StartUpdate() invoke(ClientHeartbeat.Start) end + -- 开启数据同步 + ClientDataSync.Start() + local dt = 0 -- delta time 每帧时间 local tt = 0 -- total time 游戏总时间 local now = Timer.GetTimeMillisecond --时间函数缓存 diff --git a/Code/['World']['Global']['Framework']['DataModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['DataModule'].ModuleScript.lua new file mode 100644 index 0000000..621752a --- /dev/null +++ b/Code/['World']['Global']['Framework']['DataModule'].ModuleScript.lua @@ -0,0 +1,18 @@ +--- 游戏数据 +--- @module Game Data, Both-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local Data = {} + +-- 客户端 +-- 1. Data.Global +-- 2. Data.Player + +-- 服务器 +-- 1. Data.Global +-- 2. Data.Players + +--! 这个Module为空 +--! 数据定义在:ClientDataSyncModule、ServerDataSyncModule + +return Data diff --git a/code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua similarity index 79% rename from code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua index f0a515c..5e2b5a6 100644 --- a/code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua @@ -3,8 +3,15 @@ --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang local FrameworkConfig = { + --! Debug模式 + DebugMode = true, -- 启动心跳 HeartbeatStart = true, + -- 长期存储:玩家数据定时保存时间间隔(秒) + DatabaseAutoSaveTime = 30, + -- 长期存储:重新读取游戏数据时间间隔(秒) + DatabaseReloadTimeAfterFailed = 1, + -- 服务器配置 Server = { -- 心跳包间隔时间,单位:秒 HeartbeatDelta = 1, @@ -14,13 +21,12 @@ local FrameworkConfig = { -- threshold_2 -> longer : disconnected, remove player HeartbeatThreshold1 = 5, HeartbeatThreshold2 = 10, - -- 显示心跳日志 - ShowHeartbeatLog = false, -- 插件中需要使用声明周期的服务器模块目录 PluginModules = {}, -- 插件中服务器需要生成的CustomEvent, 模块中必须得有ServerEvents PluginEvents = {} }, + -- 客户端配置 Client = { -- 心跳包间隔时间,单位:秒 HeartbeatDelta = 1, @@ -30,12 +36,17 @@ local FrameworkConfig = { -- threshold_2 -> longer : disconnected, quit server HeartbeatThreshold1 = 5, HeartbeatThreshold2 = 10, - -- 显示心跳日志 - ShowHeartbeatLog = false, -- 插件中需要使用声明周期的客户端模块目录 PluginModules = {}, -- 插件中客户端需要生成的CustomEvent,模块中必须得有ClientEvents PluginEvents = {} + }, + --! Debug相关 + Debug = { + -- 显示心跳日志 + ShowHeartbeatLog = false, + -- 显示数据同步日志 + ShowDataSyncLog = false } } diff --git a/Code/['World']['Global']['Framework']['MetaDataModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['MetaDataModule'].ModuleScript.lua new file mode 100644 index 0000000..59573aa --- /dev/null +++ b/Code/['World']['Global']['Framework']['MetaDataModule'].ModuleScript.lua @@ -0,0 +1,274 @@ +--- 游戏同步数据基类 +--- @module Sync Data Base, Both-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local MetaData = {} + +-- Localize global vars +local FrameworkConfig = FrameworkConfig + +--* 开关:Debug模式,开启后会打印日志 +local debugMode = false +--* 开关:数据校验 +local valid = true + +-- enum +MetaData.Enum = {} +-- 数据类型:全局 or 玩家 +MetaData.Enum.GLOBAL = 'Global' +MetaData.Enum.PLAYER = 'Player' + +-- 是否进行同步,数据初始化之后在开启同步 +MetaData.ServerSync = false +MetaData.ClientSync = false + +--! 说明:两种双向同步机制 +--* 1. Data.Global +-- a. 客户端和服务器持有相同的数据类型 Data.Global +-- b. C=>S,某一客户端更新,自动发送给服务器,服务器更新,然后再同步给全部客户端 +-- c. S=>C,服务器更新,广播给所有客户端,客户端各自更新 +--* 2. Data.Player +-- a. 客户端只持有自己的 Data.Player +-- b. 服务器持有全部玩家的 Data.Players +-- c. C=>S,客户端更新,自动发送给服务器,服务器更新对应玩家数据 +-- d. S=>C,服务器更新,自动发送给对应客户端,客户端更新玩家数据 + +--! 私有方法 + +--- 新建一个MetaData的proxy,用于数据同步 +-- @param _data 真实数据 +-- @param _path 当前节点索引路径 +-- @param _uid UserId +-- @return proxy 代理table,没有data,元表内包含方法和path +function NewData(_data, _path, _uid) + local proxy = {} + local mt = { + _data = _data, + _path = _path, + _uid = _uid, + __index = function(_t, _k) + local mt = getmetatable(_t) + local newpath = mt._path .. '.' .. _k + PrintLog('__index,', '_k = ', _k, ', _path = ', mt._path, ', newpath = ', newpath) + return _data[newpath] + end, + __newindex = function(_t, _k, _v) + local mt = getmetatable(_t) + local newpath = mt._path .. '.' .. _k + PrintLog('__newindex,', '_k =', _k, ', _v =', _v, ', _path = ', mt._path, ', newpath = ', newpath) + SetData(_data, newpath, _v, _uid, true) + end, + __pairs = function() + -- pairs()需要返回三个参数:next, _t, nil + -- https://www.lua.org/pil/7.3.html + -- 得到rd(raw data),从rd中进行遍历 + local rd = GetData(_data, _path) + return next, rd, nil + end + } + setmetatable(proxy, mt) + return proxy +end + +--- 获得原始数据 +-- @param _data 真实数据的存储位置 +-- @param _path 当前节点索引路径 +-- @return rawData 纯数据table,不包含元表 +function GetData(_data, _path) + local rawData = {} + GetDataAux(_data, _path, rawData) + return rawData +end + +--- GetData的辅助函数 +-- @param _data 真实数据的存储位置 +-- @param _path 当前节点索引路径 +-- @param _rawData 纯数据table,不包含元表 +function GetDataAux(_data, _path, _rawData) + local key, i + local q, elem = Queue:New(), {} + elem.path = _path + elem.rd = _rawData + q:Enqueue(elem) + while not q:IsEmpty() do + elem = q:Dequeue() + for k, v in pairs(_data) do + i = string.find(k, elem.path .. '.') + -- 筛选出当前直接层级的path,剪裁后作为rawData的key + if i == 1 and #elem.path < #k then + key = string.sub(k, #elem.path + 2, #k) + if not string.find(key, '%.') then + key = tonumber(key) or key + if type(v) == 'table' then + elem.rd[key] = {} + q:Enqueue( + { + path = k, + rd = elem.rd[key] + } + ) + else + elem.rd[key] = v + end + end + end + end + end +end + +--- 设置原始数据 +-- @param _data 真实数据的存储位置 +-- @param _path 当前节点索引路径 +-- @param _value 传入的数据 +-- @param _uid UserId +-- @param _sync true:同步数据 +function SetData(_data, _path, _value, _uid, _sync) + --* 数据同步:赋值的时候只要同步一次就可以的,存下newpath和_v,对方收到后赋值即可 + if _sync and (MetaData.ServerSync or MetaData.ClientSync) then + SyncData(_path, _value, _uid) + end + + local args, newpath = {} + + local q = Queue:New() + q:Enqueue({_data, _path, _value, _uid, _sync}) + + while not q:IsEmpty() do + _data, _path, _value, _uid, _sync = table.unpack(q:Dequeue()) + + --* 数据校验 + Validators(SetData)(_data, _path, _value, _uid, _sync) + + --* 检查现有数据 + if type(_data[_path]) == 'table' then + -- TODO: 这里可以优化,不必要每次都删除 + -- 如果现有数据是个table,删除所有子数据 + for k, _ in pairs(_data[_path]) do + -- 同等于 _data[_path][k] = nil,但是不同步 + newpath = _path .. '.' .. k + q:Enqueue({_data, newpath, nil, _uid, false}) + end + end + + --* 检查新数据 + if type(_value) == 'table' then + -- 若新数据是table,建立一个mt + _data[_path] = NewData(_data, _path, _uid) + for k, v in pairs(_value) do + -- 同等于 _data[_path][k] = v,但是不同步 + newpath = _path .. '.' .. k + q:Enqueue({_data, newpath, v, _uid, false}) + end + else + -- 一般数据,直接赋值 + _data[_path] = _value + end + end +end + +--- 数据同步 +-- @param _path 当前节点索引路径 +-- @param _value 传入的数据 +-- @param _uid UserId +function SyncData(_path, _value, _uid) + if MetaData.ServerSync and MetaData.ClientSync and localPlayer then + -- 服务器/客户端 同虚拟机 + local player = world:GetPlayerByUserId(_uid) + assert(player == localPlayer, string.format('[MetaData] 玩家不存在 uid = %s', _uid)) + PrintLog(string.format('[Server] 发出 player = %s, _path = %s, _value = %s', player, _path, table.dump(_value))) + NetUtil.Fire_C('DataSyncS2CEvent', player, _path, _value) + NetUtil.Fire_S('DataSyncC2SEvent', localPlayer, _path, _value) + elseif localPlayer == nil and string.isnilorempty(_uid) and MetaData.ServerSync then + -- 服务器 => 客户端,Global 全局数据 + NetUtil.Broadcast('DataSyncS2CEvent', _path, _value) + elseif localPlayer == nil and MetaData.ServerSync then + -- 服务器 => 客户端,Player 玩家数据 + local player = world:GetPlayerByUserId(_uid) + assert(player, string.format('[MetaData] 玩家不存在 uid = %s', _uid)) + PrintLog(string.format('[Server] 发出 player = %s, _path = %s, _value = %s', player, _path, table.dump(_value))) + NetUtil.Fire_C('DataSyncS2CEvent', player, _path, _value) + elseif localPlayer and localPlayer.UserId == _uid and MetaData.ClientSync then + -- 客户端 => 服务器 + PrintLog( + string.format('[Client] 发出 player = %s, _path = %s, _value = %s', localPlayer, _path, table.dump(_value)) + ) + NetUtil.Fire_S('DataSyncC2SEvent', localPlayer, _path, _value) + end +end + +--! 公开API + +--- 新建数据 +MetaData.New = NewData + +--- 设置数据 +MetaData.Set = SetData + +--- 从proxy中生成一个纯数据表格 +MetaData.Get = function(_proxy) + local mt = getmetatable(_proxy) + assert(mt, string.format('[MetaData] metatable为空,proxy = %s', table.dump(_proxy))) + return GetData(mt._data, mt._path) +end + +--! 辅助方法 + +--- 打印数据同步日志 +PrintLog = FrameworkConfig.DebugMode and debugMode and function(...) + print('[MetaData]', ...) + end or function() + end + +-- 数据校验 +function Validators(func) + if not valid then + return function() + end + end + + if func == SetData then + return function(_data, _path, _value, _uid, _sync) + assert( + _data, + string.format( + '[MetaData] data为空 data = %s, path = %s, uid = %s, sync = %s, value = %s', + _data, + _path, + _uid, + _sync, + table.dump(_value) + ) + ) + assert( + not string.isnilorempty(_path), + string.format( + '[MetaData] path为空 data = %s, path = %s, uid = %s, sync = %s, value = %s', + _data, + _path, + _uid, + _sync, + table.dump(_value) + ) + ) + end + end +end + +return MetaData + +--! Command Test only +--[[ +Data.Global.a = 11 +Data.Global.b = {22, 33} +Data.Global.c = {c1 = {44, 55}, c2 = 66} +Data.Global.c.c3 = {c4 = 77} +Data.Global.d = {'88', Vector3(9,9,9)} +print(table.dump(Data.Global)) +print(table.dump(MetaData.Get(Data.Global))) + +print(table.dump(Data.Player)) + +print(table.dump(Data.Players)) + +print(table.dump(Data.Players['pid:local_1'])) +]] diff --git a/code/['World']['Global']['Framework']['ServerBaseModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Server']['ServerBaseModule'].ModuleScript.lua similarity index 64% rename from code/['World']['Global']['Framework']['ServerBaseModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['Server']['ServerBaseModule'].ModuleScript.lua index 213f46e..7efdc36 100644 --- a/code/['World']['Global']['Framework']['ServerBaseModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['Server']['ServerBaseModule'].ModuleScript.lua @@ -15,4 +15,13 @@ function ServerBase:InitDefault(_module) EventUtil.LinkConnects(world.S_Event, _module, self) end +--- Debug模式下打印日志 +-- self.debug 针对模块本身的debug开关 +-- FrameworkConfig.DebugMode 框架中的全局debug开关 +function ServerBase:Log(...) + if self.debug and FrameworkConfig.DebugMode then + print(string.format('[%s]', self.name), ...) + end +end + return ServerBase diff --git a/Code/['World']['Global']['Framework']['Server']['ServerDataSyncModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Server']['ServerDataSyncModule'].ModuleScript.lua new file mode 100644 index 0000000..84322a2 --- /dev/null +++ b/Code/['World']['Global']['Framework']['Server']['ServerDataSyncModule'].ModuleScript.lua @@ -0,0 +1,286 @@ +--- 游戏服务器数据同步 +--- @module Server Sync Data, Server-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local ServerDataSync = {} + +-- Localize global vars +local FrameworkConfig, MetaData, DataStore = FrameworkConfig, MetaData, DataStore + +-- 服务器端私有数据 +local rawDataGlobal = {} +local rawDataPlayers = {} + +-- 玩家数据定时保存时间间隔(秒) +local AUTO_SAVE_TIME = FrameworkConfig.DatabaseAutoSaveTime +-- 重新读取游戏数据时间间隔(秒) +local RELOAD_TIME = 1 + +-- 玩家数据表格 +local sheet + +--- 打印数据同步日志 +local PrintLog = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowDataSyncLog and function(...) + print('[DataSync][Server]', ...) + end or function() + end + +--! 初始化 + +--- 数据初始化 +function ServerDataSync.Init() + print('[DataSync][Server] Init()') + InitEventsAndListeners() + InitDefines() + sheet = DataStore:GetSheet('PlayerData') +end + +--- 初始化事件和绑定Handler +function InitEventsAndListeners() + if world.S_Event == nil then + world:CreateObject('FolderObject', 'S_Event', world) + end + + -- 数据同步事件 + world:CreateObject('CustomEvent', 'DataSyncC2SEvent', world.S_Event) + world.S_Event.DataSyncC2SEvent:Connect(DataSyncC2SEventHandler) + + -- 玩家加入事件 + local onPlayerJoinEvent = world.S_Event.OnPlayerJoinEvent + assert(onPlayerJoinEvent, '[DataSync][Server] 不存在 OnPlayerJoinEvent') + onPlayerJoinEvent:Connect(OnPlayerJoinEventHandler) + + -- 玩家离开事件 + local onPlayerLeaveEvent = world.S_Event.OnPlayerLeaveEvent + assert(onPlayerLeaveEvent, '[DataSync][Server] 不存在 OnPlayerLeaveEvent') + onPlayerLeaveEvent:Connect(OnPlayerLeaveEventHandler) + + -- 长期存储成功事件 + if not world.S_Event.LoadPlayerDataSuccessEvent then + world:CreateObject('CustomEvent', 'LoadPlayerDataSuccessEvent', world.S_Event) + end +end + +--- 校验数据定义 +function InitDefines() + --* 服务器全局数据 + InitDataGlobal() + + --* 服务器玩家数据, key是uid + Data.Players = {} +end + +--- 初始化Data.Global +function InitDataGlobal() + --* 服务器全局数据 + Data.Global = Data.Global or MetaData.New(rawDataGlobal, MetaData.Enum.GLOBAL, nil) + -- 默认赋值 + for k, v in pairs(Data.Default.Global) do + Data.Global[k] = v + end +end + +--- 初始化Data.Players中对应玩家数据 +function InitDataPlayer(_uid) + assert(not string.isnilorempty(_uid)) + --* 服务器端创建Data.Player + local path = MetaData.Enum.PLAYER .. _uid + rawDataPlayers[_uid] = {} + Data.Players[_uid] = MetaData.New(rawDataPlayers[_uid], path, _uid) + + -- 默认赋值 + for k, v in pairs(Data.Default.Player) do + Data.Players[_uid][k] = v + end + + -- 设置uid + Data.Players[_uid].uid = _uid +end + +--- 开始同步 +function ServerDataSync.Start() + print('[DataSync][Server] 服务器数据同步开启') + MetaData.ServerSync = true + + -- 启动定时器 + TimeUtil.SetInterval(SaveAllGameDataAsync, AUTO_SAVE_TIME) +end + +--! 长期存储:读取 + +--- 下载玩家的游戏数据 +--- @param _uid string 玩家ID +function LoadGameDataAsync(_uid) + sheet = DataStore:GetSheet('PlayerData') + assert(sheet, '[DataSync][Server] DataPlayers的sheet不存在') + sheet:GetValue( + _uid, + function(_val, _msg) + LoadGameDataAsyncCb(_val, _msg, _uid) + end + ) +end + +--- 下载玩家的游戏数据回调 +--- @param _val table 数据 +--- @param _msg int 消息码 +--- @param _uid string 玩家ID +function LoadGameDataAsyncCb(_val, _msg, _uid) + local player = world:GetPlayerByUserId(_uid) + assert(player, string.format('[DataSync][Server] 玩家不存在, uid = %s', _uid)) + if _msg == 0 or _msg == 101 then + print('[DataSync][Server] 获取玩家数据成功', player.Name) + local hasData = _val ~= nil + if hasData then + print('[DataSync][Server] 玩家数据,存在', player.Name) + --若以前的数据存在,更新 + -- TODO: 数据兼容的处理 + local data = _val + assert(data.uid == _uid, string.format('[DataSync][Server] uid校验不通过, uid = %s', _uid)) + --若已在此服务器的数据总表存在,则更新数据 + for k, v in pairs(data) do + Data.Players[_uid][k] = data[k] + end + else + -- 不存在数据,用之前生成的默认数据 + print('[DataSync][Server] 玩家数据,不存在', player.Name) + end + NetUtil.Fire_S('LoadPlayerDataSuccessEvent', player, hasData) + NetUtil.Fire_C('LoadPlayerDataSuccessEvent', player, hasData) + return + end + print( + string.format( + '[DataSync][Server] 获取玩家数据失败,%s秒后重试, uid = %s, player = %s, msg = %s', + RELOAD_TIME, + _uid, + player.Name, + _msg + ) + ) + --若失败,则1秒后重新再读取一次 + invoke( + function() + LoadGameDataAsync(_uid) + end, + RELOAD_TIME + ) +end + +--! 长期存储:保存 + +--- 上传玩家的游戏数据 +--- @param _userId string 玩家ID +--- @param _delete string 保存成功后是否删除缓存数据 +function SaveGameDataAsync(_uid, _delete) + sheet = DataStore:GetSheet('PlayerData') + assert(sheet, '[DataSync][Server] DataPlayers的sheet不存在') + assert(not string.isnilorempty(_uid), '[DataSync][Server] uid不存在或为空') + assert(Data.Players[_uid], string.format('[DataSync][Server] Data.Players[_uid]不存在 uid = %s', _uid)) + local newData = MetaData.Get(Data.Players[_uid]) + assert(newData, string.format('[DataSync][Server] 玩家数据不存在, uid = %s', _uid)) + assert(newData.uid == _uid, string.format('[DataSync][Server] uid校验不通过, uid = %s', _uid)) + sheet:SetValue( + _uid, + newData, + function(_val, _msg) + SaveGameDataAsyncCb(_val, _msg, _uid, _delete) + end + ) +end + +--- 上传玩家的游戏数据回调 +--- @param _val table 数据 +--- @param _msg int 消息码 +--- @param _uid string 玩家ID +function SaveGameDataAsyncCb(_val, _msg, _uid, _delete) + -- 保存成功 + if _msg == 0 then + print('[DataSync][Server] 保存玩家数据,成功', _uid) + if _delete == true then + print('[DataSync][Server] 删除服务器玩家数据', _uid) + rawDataPlayers[_uid] = nil + --* 删除玩家端数据 + Data.Players[_uid] = nil + end + return + end + + -- 保存失败 + print(string.format('[DataSync][Server] 保存玩家数据失败,%s秒后重试, uid = %s, msg = %s', RELOAD_TIME, _uid, _msg)) + --若失败,则1秒后重新再读取一次 + invoke( + function() + SaveGameDataAsync(_uid, _delete) + end, + RELOAD_TIME + ) +end + +--- 存储全部玩家数据 +function SaveAllGameDataAsync() + if not MetaData.ServerSync then + print('[DataSync][Server] ServerSync未开始') + return + end + print('[DataSync][Server] 尝试保存全部玩家数据……') + for uid, data in pairs(Data.Players) do + if not string.isnilorempty(uid) and data then + SaveGameDataAsync(uid, false) + end + end +end + +--! Event handler + +--- 数据同步事件Handler +function DataSyncC2SEventHandler(_player, _path, _value) + if not MetaData.ServerSync then + return + end + + PrintLog(string.format('收到 player = %s, _path = %s, _value = %s', _player, _path, table.dump(_value))) + + local uid = _player.UserId + + if string.startswith(_path, MetaData.Enum.GLOBAL) then + --* Data.Global:收到客户端改变数据的时候需要同步给其他玩家 + MetaData.Set(rawDataGlobal, _path, _value, nil, true) + elseif string.startswith(_path, MetaData.Enum.PLAYER .. uid) then + --* Data.Players + MetaData.Set(rawDataPlayers[uid], _path, _value, uid, false) + else + error( + string.format( + '[DataSync][Server] _path错误 _player = %s, _path = %s, _value = %s', + _player, + _path, + table.dump(_data) + ) + ) + end +end + +--- 新玩家加入事件Handler +function OnPlayerJoinEventHandler(_player, _uid) + print('[DataSync][Server] OnPlayerJoinEventHandler', _player, _player.UserId, _uid) + + --* 向客户端同步Data.Global + NetUtil.Fire_C('DataSyncS2CEvent', _player, MetaData.Enum.GLOBAL, MetaData.Get(Data.Global)) + + -- 初始化玩家数据 + InitDataPlayer(_uid) + + --* 获取长期存储,成功后向客户端同步 + LoadGameDataAsync(_uid) +end + +--- 玩家离开事件Handler +function OnPlayerLeaveEventHandler(_player, _uid) + print('[DataSync][Server] OnPlayerLeaveEventHandler', _player, _uid) + assert(not string.isnilorempty(_uid), '[ServerDataSync] OnPlayerLeaveEventHandler() uid不存在') + --* 保存长期存储:rawDataPlayers[_uid] 保存成功后删掉 + SaveGameDataAsync(_uid, true) +end + +return ServerDataSync diff --git a/code/['World']['Global']['Framework']['ServerHeartbeatModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Server']['ServerHeartbeatModule'].ModuleScript.lua similarity index 77% rename from code/['World']['Global']['Framework']['ServerHeartbeatModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['Server']['ServerHeartbeatModule'].ModuleScript.lua index 516c596..2b25e73 100644 --- a/code/['World']['Global']['Framework']['ServerHeartbeatModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['Server']['ServerHeartbeatModule'].ModuleScript.lua @@ -5,17 +5,17 @@ local ServerHeartbeat = {} -- Localize global vars -local Setting = FrameworkConfig.Server +local FrameworkConfig = FrameworkConfig -- 心跳包间隔时间,单位:秒 -local HEARTBEAT_DELTA = Setting.HeartbeatDelta +local HEARTBEAT_DELTA = FrameworkConfig.Server.HeartbeatDelta -- 心跳阈值,单位:秒,范围定义如下: -- 0s -> threshold_1 : connected -- threshold_1 -> threshold_2 : disconnected, but player can rejoin -- threshold_2 -> longer : disconnected, remove player -local HEARTBEAT_THRESHOLD_1 = Setting.HeartbeatThreshold1 * 1000 -- second => ms -local HEARTBEAT_THRESHOLD_2 = Setting.HeartbeatThreshold2 * 1000 -- second => ms +local HEARTBEAT_THRESHOLD_1 = FrameworkConfig.Server.HeartbeatThreshold1 * 1000 -- second => ms +local HEARTBEAT_THRESHOLD_2 = FrameworkConfig.Server.HeartbeatThreshold2 * 1000 -- second => ms -- 玩家心跳连接状态 local HeartbeatEnum = { @@ -34,7 +34,7 @@ local diff -- 时间戳插值 local sTmpTs, cTmpTs -- 时间戳缓存 --- 打印心跳日志 -local PrintHb = Setting.ShowHeartbeatLog and function(...) +local PrintHb = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowHeartbeatLog and function(...) print('[Heartbeat][Server]', ...) end or function() end @@ -100,9 +100,12 @@ function InitEventsAndListeners() -- 玩家退出,发出OnPlayerLeaveEvent world.OnPlayerRemoved:Connect( function(_player) - if cache[_player] then - print('[Heartbeat][Server] OnPlayerLeaveEvent, 玩家主动离开游戏,', _player) - NetUtil.Fire_S('OnPlayerLeaveEvent', _player) + local player = _player + local uid = player.UserId + if cache[player] then + print('[Heartbeat][Server] OnPlayerLeaveEvent, 玩家主动离开游戏,', player, uid) + NetUtil.Fire_S('OnPlayerLeaveEvent', player, uid) + cache[player] = nil end end ) @@ -140,14 +143,14 @@ function CheckPlayerJoin(_player) if not cache[_player] then --* 玩家新加入 OnPlayerJoinEvent print('[Heartbeat][Server] OnPlayerJoinEvent, 新玩家加入,', _player) - NetUtil.Fire_S('OnPlayerJoinEvent', _player) + NetUtil.Fire_S('OnPlayerJoinEvent', _player, _player.UserId) cache[_player] = { state = HeartbeatEnum.CONNECT } elseif cache[_player].state == HeartbeatEnum.DISCONNECT then --* 玩家断线重连 OnPlayerReconnectEvent print('[Heartbeat][Server] OnPlayerReconnectEvent, 玩家断线重连,', _player) - NetUtil.Fire_S('OnPlayerReconnectEvent', _player) + NetUtil.Fire_S('OnPlayerReconnectEvent', _player, _player.UserId) cache[_player].state = HeartbeatEnum.CONNECT end end @@ -159,19 +162,23 @@ function CheckPlayerStates(_player, _sTimestam) end diff = _sTimestam - cache[_player].sTimestamp PrintHb(string.format('==========================================> diff = %s, %s', diff * .001, _player)) - - if cache[_player].state == HeartbeatEnum.CONNECT and diff > HEARTBEAT_THRESHOLD_1 then - --* 玩家断线 OnPlayerReconnectEvent - print('[Heartbeat][Server] OnPlayerDisconnectEvent, 玩家离线, 等待断线重连,', _player) - NetUtil.Fire_S('OnPlayerDisconnectEvent', _player) + if diff < HEARTBEAT_THRESHOLD_1 then + --* 玩家在线 + cache[_player].state = HeartbeatEnum.CONNECT + elseif cache[_player].state == HeartbeatEnum.CONNECT and diff >= HEARTBEAT_THRESHOLD_1 then + --* 玩家断线 OnPlayerDisconnectEvent + print('[Heartbeat][Server] OnPlayerDisconnectEvent, 玩家离线, 等待断线重连,', _player, _player.UserId) + NetUtil.Fire_S('OnPlayerDisconnectEvent', _player, _player.UserId) cache[_player].state = HeartbeatEnum.DISCONNECT - elseif cache[_player].state == HeartbeatEnum.DISCONNECT and diff > HEARTBEAT_THRESHOLD_2 then + elseif cache[_player].state == HeartbeatEnum.DISCONNECT and diff >= HEARTBEAT_THRESHOLD_2 then --* 玩家彻底断线,剔除玩家 - print('[Heartbeat][Server] OnPlayerLeaveEvent, 剔除离线玩家,', _player) - NetUtil.Fire_S('OnPlayerLeaveEvent', _player) - print('[Heartbeat][Server] OnPlayerLeave, 发送客户端离线事件,', _player) - NetUtil.Fire_C('OnPlayerLeaveEvent', _player) - cache[_player] = nil + local player = _player + local uid = player.UserId + print('[Heartbeat][Server] OnPlayerLeaveEvent, 剔除离线玩家,', player, uid) + NetUtil.Fire_S('OnPlayerLeaveEvent', player, uid) + print('[Heartbeat][Server] OnPlayerLeaveEvent, 发送客户端离线事件,', player, uid) + NetUtil.Fire_C('OnPlayerLeaveEvent', player, uid) + cache[player] = nil end end diff --git a/code/['World']['Global']['Framework']['ServerModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Server']['ServerModule'].ModuleScript.lua similarity index 84% rename from code/['World']['Global']['Framework']['ServerModule'].ModuleScript.lua rename to Code/['World']['Global']['Framework']['Server']['ServerModule'].ModuleScript.lua index 199de55..992d896 100644 --- a/code/['World']['Global']['Framework']['ServerModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Framework']['Server']['ServerModule'].ModuleScript.lua @@ -36,6 +36,7 @@ function InitServer() print('[Server] InitServer()') InitRandomSeed() InitHeartbeat() + InitDataSync() InitServerCustomEvents() InitCsvAndXls() GenInitAndUpdateList() @@ -76,6 +77,12 @@ function InitHeartbeat() ServerHeartbeat.Init() end +--- 初始化数据同步 +function InitDataSync() + assert(ServerDataSync, '[Server][DataSync] 找不到ServerDataSync,请联系张远程') + ServerDataSync.Init() +end + --- 生成框架需要的节点 function InitCsvAndXls() if not world.Global.Csv then @@ -88,9 +95,15 @@ end --- 生成需要Init和Update的模块列表 function GenInitAndUpdateList() + -- TODO: 改成在FrameworkConfig中配置 + -- Init Default ModuleUtil.GetModuleListWithFunc(Module.S_Module, 'InitDefault', initDefaultList) + -- Init + ModuleUtil.GetModuleListWithFunc(Define, 'Init', initList) ModuleUtil.GetModuleListWithFunc(Module.S_Module, 'Init', initList) + -- Update ModuleUtil.GetModuleListWithFunc(Module.S_Module, 'Update', updateList) + -- Plugin for _, m in pairs(FrameworkConfig.Server.PluginModules) do ModuleUtil.GetModuleListWithFunc(m, 'InitDefault', initDefaultList) ModuleUtil.GetModuleListWithFunc(m, 'Init', initList) @@ -129,6 +142,9 @@ function StartUpdate() invoke(ServerHeartbeat.Start) end + -- 开启数据同步 + ServerDataSync.Start() + local dt = 0 -- delta time 每帧时间 local tt = 0 -- total time 游戏总时间 local now = Timer.GetTimeMillisecond --时间函数缓存 @@ -140,9 +156,24 @@ function StartUpdate() tt = tt + dt prev = curr UpdateServer(dt, tt) + --[[xpcall( + function() + --local a = 10 / nil + + end, + function(err) + ErrorShow(err) + error(err) + end + )]] end end +function ErrorShow(err) + world.Global.ErrorGUI:SetActive(true) + world.Global.ErrorGUI.Error.Text = err +end + --- Update函数 --- @param dt delta time 每帧时间 function UpdateServer(_dt, _tt) diff --git a/code/['World']['Global']['LuaFunctionScript'].Script.lua b/Code/['World']['Global']['LuaFunctionScript'].Script.lua similarity index 99% rename from code/['World']['Global']['LuaFunctionScript'].Script.lua rename to Code/['World']['Global']['LuaFunctionScript'].Script.lua index 8dfae50..6c88ae9 100644 --- a/code/['World']['Global']['LuaFunctionScript'].Script.lua +++ b/Code/['World']['Global']['LuaFunctionScript'].Script.lua @@ -907,7 +907,7 @@ end function Stack:Pop() if self:IsEmpty() then - --print("Error: the stack is empty") + print('Error: the stack is empty') return end local value = self._stack[self._last] @@ -956,3 +956,13 @@ function Stack:PrintElement() str = str .. '}' print(str) end + +--- uid递增计数器 +--- @author Sid Zhang +function UidCounter() + local num = 0 + return function() + num = num + 1 + return string.format('%s-%s', os.clock(), num) + end +end diff --git a/code/['World']['Global']['Module']['C_Module']['LanguageUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Module']['C_Module']['LanguageUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Module']['C_Module']['LanguageUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Module']['C_Module']['LanguageUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Module']['C_Module']['PlayerControlModule'].ModuleScript.lua b/Code/['World']['Global']['Module']['C_Module']['PlayerControlModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Module']['C_Module']['PlayerControlModule'].ModuleScript.lua rename to Code/['World']['Global']['Module']['C_Module']['PlayerControlModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Module']['C_Module']['PlayerGuiDefaultModule'].ModuleScript.lua b/Code/['World']['Global']['Module']['C_Module']['PlayerGuiDefaultModule'].ModuleScript.lua similarity index 75% rename from code/['World']['Global']['Module']['C_Module']['PlayerGuiDefaultModule'].ModuleScript.lua rename to Code/['World']['Global']['Module']['C_Module']['PlayerGuiDefaultModule'].ModuleScript.lua index 5a96144..a2b09a0 100644 --- a/code/['World']['Global']['Module']['C_Module']['PlayerGuiDefaultModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Module']['C_Module']['PlayerGuiDefaultModule'].ModuleScript.lua @@ -7,10 +7,10 @@ local PlayerGuiDefault, this = ModuleUtil.New('PlayerGuiDefault', ClientBase) local player -- 姓名板 -local nameGUI +local nameGui -- 血条 -local healthGUI, background, healthBar +local healthGui, background, healthBar local RED_BAR = ResourceManager.GetTexture('Internal/Blood_Red') local GREEN_BAR = ResourceManager.GetTexture('Internal/Blood_Green') local ORANGE_BAR = ResourceManager.GetTexture('Internal/Blood_Orange') @@ -27,30 +27,29 @@ end -- 姓名板 function PlayerGuiDefault:InitNameGui() - nameGUI = player.NameGui - nameGUI.NameBarTxt1.Text = player.Name - nameGUI.NameBarTxt2.Text = player.Name + nameGui = player.NameGui + nameGui.NameBarTxt.Text = player.Name end -- 血条 function PlayerGuiDefault:InitHealthBarGui() - healthGUI = player.HealthGui - background = healthGUI.BackgroundImg + healthGui = player.HealthGui + background = healthGui.BackgroundImg healthBar = background.HealthBarImg end -- 初始化事件 function PlayerGuiDefault:InitListener() player.OnHealthChange:Connect(HealthChange) - world.OnRenderStepped:Connect(MainGUI) + world.OnRenderStepped:Connect(MainGui) end -- 姓名板的显示逻辑 function NameBarLogic() - nameGUI.Visible = player.DisplayName + nameGui.Visible = player.DisplayName if player.DisplayName then - local addedHeight = (healthGUI and healthGUI.ActiveSelf) and 1.1 or 1 - nameGUI.LocalPosition = Vector3(0, addedHeight + player.Avatar.Height, 0) + local addedHeight = (healthGui and healthGui.ActiveSelf) and 1.1 or 1 + nameGui.LocalPosition = Vector3(0, addedHeight + player.Avatar.Height, 0) end end @@ -74,18 +73,18 @@ end function HealthBarLogic(_delta) healthBarShowTime = healthBarShowTime - _delta if player.HealthDisplayMode == Enum.HealthDisplayMode.Always then - healthGUI.Visible = true + healthGui.Visible = true elseif player.HealthDisplayMode == Enum.HealthDisplayMode.Never then - healthGUI.Visible = false + healthGui.Visible = false elseif player.HealthDisplayMode == Enum.HealthDisplayMode.OnHit then - healthGUI.Visible = player.Health ~= player.MaxHealth + healthGui.Visible = player.Health ~= player.MaxHealth else - healthGUI.Visible = healthBarShowTime > 0 + healthGui.Visible = healthBarShowTime > 0 end end -- 每个渲染帧更新姓名板和血条的显示逻辑 -function MainGUI(_delta) +function MainGui(_delta) NameBarLogic() HealthBarLogic(_delta) end diff --git a/Code/['World']['Global']['Module']['Editor_Module']['SceneMeshEditorModule'].ModuleScript.lua b/Code/['World']['Global']['Module']['Editor_Module']['SceneMeshEditorModule'].ModuleScript.lua new file mode 100644 index 0000000..02d7657 --- /dev/null +++ b/Code/['World']['Global']['Module']['Editor_Module']['SceneMeshEditorModule'].ModuleScript.lua @@ -0,0 +1,29 @@ +--- 用于场景mesh的一些快捷编辑功能 +--- @module SceneMeshEditor +--- @copyright Lilith Games, Avatar Team +--- @author Sid Zhang +local SceneMeshEditor = {} + +--- 将当前选中节点下的所有特定名称的mesh节点更改其引用 +--- @param _NodeName string 需要被替换资源的节点名字 +--- @param _MeshResource string FBX资源路径 +--- @param _root object 执行该操作的根节点,缺省则为选中的第一个节点 +function SceneMeshEditor:ChangeMesh(_NodeName, _MeshResource, _root) + if not _root then + _root = Editor.Selections[1] + end + if not _root then + print('[SceneMeshEditor] 没有选中或输入任何节点') + return + end + for _, v in pairs(_root:GetChildren()) do + if v.Name == _NodeName and v.ClassName == 'MeshObject' then + v.Mesh = ResourceManager.GetMesh(_MeshResource) + end + self:ChangeMesh(_NodeName, _MeshResource, v) + end +end + +return SceneMeshEditor + +-- require(Module.Editor_Module.SceneMeshEditorModule):ChangeMesh() diff --git a/code/['World']['Global']['ModuleRequireScript'].Script.lua b/Code/['World']['Global']['ModuleRequireScript'].Script.lua similarity index 84% rename from code/['World']['Global']['ModuleRequireScript'].Script.lua rename to Code/['World']['Global']['ModuleRequireScript'].Script.lua index 6b39fcf..825eb1e 100644 --- a/code/['World']['Global']['ModuleRequireScript'].Script.lua +++ b/Code/['World']['Global']['ModuleRequireScript'].Script.lua @@ -2,6 +2,10 @@ -- @script Module Defines -- @copyright Lilith Games, Avatar Team +-- Game Defines +GAME_ID = 'X0000' +print('GAME_ID = ', GAME_ID) + -- Utilities ModuleUtil = require(Utility.ModuleUtilModule) LuaJsonUtil = require(Utility.LuaJsonUtilModule) @@ -16,16 +20,17 @@ LinkedList = Utility.LinkedListModule ValueChangeUtil = require(Utility.ValueChangeUtilModule) TimeUtil = require(Utility.TimeUtilModule) CloudLogUtil = require(Utility.CloudLogUtilModule) +ObjPoolUtil = require(Utility.ObjPoolUtilModule) +SoundUtil = require(Utility.SoundUtilModule) --- Game Defines -GAME_ID = 'X0000' - --- Utility Initilization +-- Init Utilities TimeUtil.Init() CloudLogUtil.Init(GAME_ID) -- Framework ModuleUtil.LoadModules(Framework) +ModuleUtil.LoadModules(Framework.Server) +ModuleUtil.LoadModules(Framework.Client) -- Globle Defines ModuleUtil.LoadModules(Define) diff --git a/code/['World']['Global']['Plugin']['FUNC_Guide']['GuideSystemModule'].ModuleScript.lua b/Code/['World']['Global']['Plugin']['FUNC_Guide']['GuideSystemModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Plugin']['FUNC_Guide']['GuideSystemModule'].ModuleScript.lua rename to Code/['World']['Global']['Plugin']['FUNC_Guide']['GuideSystemModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua similarity index 54% rename from code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua index 702cfd9..65f6636 100644 --- a/code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua @@ -1,7 +1,7 @@ --- 埋点数据工具 --- @module CloudLogUtil --- @copyright Lilith Games, Avatar Team ---- @author Sharif Ma +--- @author Sharif Ma, Xinwu Zhang local CloudLogUtil = {} ---埋点工具初始化 @@ -12,14 +12,13 @@ end ---触发埋点相应的事件调用 ---@param _key string 埋点的键 -function CloudLogUtil.UploadLog(_key, ...) - local tableName = CloudLogUtil.gameId .. '_' .. _key - local args = { ... } +function CloudLogUtil.UploadLog(_key, _table) + local arg = LuaJsonUtil:encode(_table) if localPlayer then - TrackService.CloudLogFromClient({ tableName, CloudLogUtil.gameId, table.unpack(args) }) + TrackService.CloudLogFromClient({_key, CloudLogUtil.gameId, arg}) else - TrackService.CloudLogFromServer({ tableName, CloudLogUtil.gameId, table.unpack(args) }) + TrackService.CloudLogFromServer({_key, CloudLogUtil.gameId, arg}) end end -return CloudLogUtil \ No newline at end of file +return CloudLogUtil diff --git a/code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua similarity index 66% rename from code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua index f5c4474..a294671 100644 --- a/code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua @@ -1,7 +1,7 @@ --- 全局函数的定义 --- @module GlobalFunc Defines --- @copyright Lilith Games, Avatar Team ---- @author Sid Zhang +--- @author Sid Zhang, Yuancheng Zhang local GlobalFunc = {} --- 埋点上传日志 @@ -23,4 +23,11 @@ function GlobalFunc.UploadLogs(_tableName, ...) end end +-- 检查碰撞对象是否为NPC +-- Server-side 一般用于服务器端 +function GlobalFunc.CheckHitObjIsPlayer(_hitObj) + return _hitObj and _hitObj.ClassName == 'PlayerInstance' and _hitObj.Avatar and + _hitObj.Avatar.ClassName == 'PlayerAvatarInstance' +end + return GlobalFunc diff --git a/code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['LuaJsonUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['LuaJsonUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['LuaJsonUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['LuaJsonUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua similarity index 82% rename from code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua index fc900a4..6fd959e 100644 --- a/code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua @@ -11,23 +11,26 @@ local ModuleUtil = {} function ModuleUtil.LoadModules(_root, _scope) _scope = _scope or _G assert(_root, '[ModuleUtil] Node does NOT exist!') - local tmp = _root:GetChildren() + local tmp, name = _root:GetChildren() for _, v in pairs(tmp) do - name = (v.Name):gsub('Module', '') - -- print('[ModuleUtil] Load: ' .. name) - _scope[name] = require(v) + if v.ClassName == 'ModuleScriptObject' then + name = (v.Name):gsub('Module', '') + print('[ModuleUtil] Load Module: ', name) + _scope[name] = require(v) + end end end --- 加载XLS表格目录 -- @param _root 模块目录的节点 +-- @param _config 所有Excel生成Lua文件的所在table,不允许是_G function ModuleUtil.LoadXlsModules(_root, _config) assert(_root, '[ModuleUtil] Node does NOT exist!') assert(_config, '[ModuleUtil] Config does NOT exist!') - local tmp = _root:GetChildren() + local tmp, name = _root:GetChildren() for _, v in pairs(tmp) do name = (v.Name):gsub('XlsModule', '') - print('[ModuleUtil] Load: ' .. name) + print('[ModuleUtil] Load XLS: ', name) _config[name] = require(v) end end diff --git a/code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua similarity index 84% rename from code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua index ec3ddaa..7e4ccdd 100644 --- a/code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua @@ -36,9 +36,9 @@ end -- @param _player 玩家对象 -- @param ... 事件参数 function NetUtil.Fire_C(_eventName, _player, ...) - if(_player == nil) then - return - end + if _player == nil then + return + end ValidateArgs(FireEnum.CLIENT, _eventName, _player) local args = {...} _player.C_Event[_eventName]:Fire(table.unpack(args)) @@ -85,6 +85,7 @@ ValidateArgs = _player and _player.ClassName == 'PlayerInstance', string.format('[NetUtil][Fire_C]第2个参数需要是玩家对象, 错误事件: %s', _eventName) ) + assert(_player.C_Event, '[NetUtil][Fire_C]第2个参数需要是玩家对象, 错误事件: %s', _eventName) assert( _player.C_Event[_eventName], string.format('[NetUtil][Fire_C] 客户端玩家不存在事件: %s, 玩家: %s', _player.Name, _eventName) @@ -98,15 +99,17 @@ ValidateArgs = end --- 打印事件日志 -PrintEventLog = showLog and function(_fireEnum, _eventName, _player, _args) +PrintEventLog = + showLog and + function(_fireEnum, _eventName, _player, _args) if _fireEnum == FireEnum.SERVER then --* Fire_S 参数打印 - print(string.format('[NetUtil][服务器] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) + print(string.format('[NetUtil][发出服务器事件] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) elseif _fireEnum == FireEnum.CLIENT then --* Fire_C 参数打印 print( string.format( - '[NetUtil][客户端] %s, 玩家=%s, 参数 = %s, %s', + '[NetUtil][发出客户端事件] %s, 玩家=%s, 参数 = %s, %s', _eventName, _player.Name, #_args, @@ -114,10 +117,11 @@ PrintEventLog = showLog and function(_fireEnum, _eventName, _player, _args) ) ) elseif _fireEnum == FireEnum.BROADCAST then - --* Broadcase 参数打印 - print(string.format('[NetUtil][客户端][广播] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) + --* Broadcast 参数打印 + print(string.format('[NetUtil][发出客户端广播事件] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) end - end or function() + end or + function() end return NetUtil diff --git a/code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua similarity index 78% rename from code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua index b276648..defe937 100644 --- a/code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua @@ -1,8 +1,7 @@ ---对象池工具模块 ----@module ObjPoolUtil +---@class ObjPoolUtil -- @copyright Lilith Games, Avatar Team -- @author Yen Yuan ----@class ObjPoolUtil local ObjPoolUtil = class('ObjPoolUtil') ---创建某一个对象的对象池 @@ -26,6 +25,17 @@ function ObjPoolUtil.static.Newpool(_folderName, _objName, _maxCount) return realPool end +---从池中预创建对象到世界下 +---@param _position Vector3 +function ObjPoolUtil:PreSpawn(_position) + for i = 1, self.maxCount do + local realObj = world:CreateInstance(self.obj, self.obj, self.folder, _position) + realObj.IsStatic = true + table.insert(self.pool, realObj) + realObj:SetActive(false) + end +end + ---从池中创建对象到世界下 ---@param _position Vector3 ---@param _rotation EulerDegree @@ -41,7 +51,8 @@ function ObjPoolUtil:Spawn(_position, _rotation) else realObj = self.pool[1] self.pool[1].Position = _position - self.pool[1].Rotation = _rotation + self.pool[1].Rotation = _rotation or EulerDegree(0, 0, 0) + self.pool[1].IsStatic = false self.pool[1]:SetActive(true) table.remove(self.pool, 1) return realObj @@ -54,11 +65,12 @@ function ObjPoolUtil:Despawn(_obj) if _obj == nil then error('[ObjPoolUtil] 传入对象为空') elseif #self.pool > self.maxCount then - error(string.format('[ObjPoolUtil] %s对象池已满,该对象会永久销毁', self.obj)) + print(string.format('[ObjPoolUtil] %s对象池已满,该对象会永久销毁', self.obj)) _obj:Destroy() else table.insert(self.pool, _obj) _obj:SetActive(false) + self.pool[1].IsStatic = true end end diff --git a/Code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua new file mode 100644 index 0000000..7342ee2 --- /dev/null +++ b/Code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua @@ -0,0 +1,178 @@ +--- 音效播放模块 +--- @module SoundUtil +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local SoundUtil = {} + +--音频 +local clipTable = nil + +--音效对象池 +local audioSourcePool = { + SE3D = {}, --3D音效 + SE2D = {} --2D音效 +} + +--3D音效对象池上限 +local SE3DMax = 8 +--2D音效对象池上限 +local SE2DMax = 3 + +--! Debug模式 +local DebugMode = true + +--- 打印日志 +local PrintLog = DebugMode and function(...) + print('[SoundUtil]', ...) + end or function() + end + +--初始化音频表 +local function InitClipTable(_data) + clipTable[_data.ID] = { + id = _data.ID, + clip = ResourceManager.GetSoundClip(_data.Path), + isLoop = _data.IsLoop, + volume = _data.Volume, + minDistance = _data.MinDistance, + maxDistance = _data.MaxDistance + } + --PrintLog(table.dump(clipTable[_data.ID])) +end + +--初始化一个2D播放器 +local function Init2DAudioSource(_index, _uid) + audioSourcePool.SE2D[_uid][_index] = + world:CreateObject( + 'AudioSource', + 'AudioSource' .. _index, + world:GetPlayerByUserId(_uid).Local.Independent.GameCam.SENode, + world:GetPlayerByUserId(_uid).Local.Independent.GameCam.SENode.Position + ) + return audioSourcePool.SE2D[_uid][_index] +end + +--初始化一个3D播放器 +local function Init3DAudioSource(_index) + audioSourcePool.SE3D[_index] = world:CreateObject('AudioSource', 'AudioSource' .. _index, world.SENode) + return audioSourcePool.SE3D[_index] +end + +function SoundUtil.Init(_config) + print('[SoundUtil] Init(_config)') + if clipTable == nil then + clipTable = {} + for k, v in pairs(_config) do + InitClipTable(v) + end + end +end + +--初始化音效播放器对象池 +function SoundUtil.InitAudioSource(_uid) + if _uid then + audioSourcePool.SE2D[_uid] = {} + for i = 1, SE2DMax do + Init2DAudioSource(i, _uid) + end + else + for i = 1, SE3DMax do + Init3DAudioSource(i) + end + end +end + +--释放多余播放器 +local function ReleaeseSource(_uid) + local index = 0 + if _uid then + index = SE2DMax + 1 + while table.nums(audioSourcePool.SE2D[_uid]) > index - 1 do + if audioSourcePool.SE2D[_uid][index].State == Enum.AudioSourceState.Stopped then + local ReleaesedSource = audioSourcePool.SE2D[_uid][index] + table.remove(audioSourcePool.SE2D[_uid], index) + ReleaesedSource:Destroy() + else + index = index + 1 + end + end + else + index = SE3DMax + 1 + while table.nums(audioSourcePool.SE3D) > index - 1 do + if audioSourcePool.SE3D[index].State == Enum.AudioSourceState.Stopped then + local ReleaesedSource = audioSourcePool.SE3D[index] + table.remove(audioSourcePool.SE3D, index) + ReleaesedSource:Destroy() + else + index = index + 1 + end + end + end +end + +--播放2D音频 +function SoundUtil.Play2DSE(_uid, _SEID) + local index = nil + local source = nil + for k, v in pairs(audioSourcePool.SE2D[_uid]) do + if v.State == Enum.AudioSourceState.Stopped then + index = k + source = v + break + end + end + if source == nil then + index = table.nums(audioSourcePool.SE2D[_uid]) + 1 + source = Init2DAudioSource(table.nums(audioSourcePool.SE2D[_uid]) + 1, _uid) + end + --PrintLog('播放2D音频', _SEID) + source.MinDistance = clipTable[_SEID].minDistance + source.MaxDistance = clipTable[_SEID].maxDistance + source.Loop = clipTable[_SEID].isLoop + source.Volume = clipTable[_SEID].volume + source.SoundClip = clipTable[_SEID].clip + source:Play() + ReleaeseSource(_uid) + return index +end + +--播放3D音频 +function SoundUtil.Play3DSE(_pos, _SEID) + local index = nil + local source = nil + for k, v in pairs(audioSourcePool.SE3D) do + if v.State == Enum.AudioSourceState.Stopped then + index = k + source = v + break + end + end + if source == nil then + index = table.nums(audioSourcePool.SE3D) + 1 + source = Init3DAudioSource(table.nums(audioSourcePool.SE3D) + 1) + end + --PrintLog('播放3D音频', _SEID, table.dump(clipTable[_SEID]), _pos) + source.Position = _pos + source.MinDistance = clipTable[_SEID].minDistance + source.MaxDistance = clipTable[_SEID].maxDistance + source.Loop = clipTable[_SEID].isLoop + source.Volume = clipTable[_SEID].volume + source.SoundClip = clipTable[_SEID].clip + source:Play() + ReleaeseSource() + return index +end + +--停止播放2D音频 +function SoundUtil.Stop2DSE(_uid, _index) + audioSourcePool.SE2D[_uid][_index]:Stop() + ReleaeseSource(_uid) +end + +--停止播放3D音频 +function SoundUtil.Stop3DSE(_index) + audioSourcePool.SE3D[_index]:Stop() + ReleaeseSource() +end + +return SoundUtil diff --git a/code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua similarity index 80% rename from code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua index 65d83de..150d975 100644 --- a/code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua @@ -20,41 +20,42 @@ local DELTA_TIME = .05 --- Find all registered events to trigger local function CheckEvents() -- now = os.time() - local now = Timer.GetTimeMillisecond() - local i, event = 1 - while i <= #eventList do + local now = Timer.GetTime() + local event + for i = #eventList, 1, -1 do event = eventList[i] if event.triggerTime <= now then table.insert(activeEvents, event) if event.loop then event.triggerTime = event.triggerTime + event.delay - i = i + 1 else table.remove(eventList, i) end - else - i = i + 1 end end end --- Trigger events local function TriggerEvents() - local i = 1 - while i <= #activeEvents do + local event + for i = #activeEvents, 1, -1 do event = activeEvents[i] - invoke(event.func) + invoke( + function() + event.func() + end + ) table.remove(activeEvents, i) end + assert(#activeEvents == 0, string.format('[TimeUtil] 有未执行的事件%s个', #activeEvents)) end --- Update local function StartUpdate() - while running do - -- print(os.time()) + while running and wait(DELTA_TIME) do + -- print(Timer.GetTime(), os.time()) CheckEvents() TriggerEvents() - wait(DELTA_TIME) end end @@ -65,6 +66,9 @@ end --- Run Update() function TimeUtil.Start() + if running then + return + end running = true invoke(StartUpdate) end @@ -82,6 +86,7 @@ end -- @see https://www.w3schools.com/jsref/met_win_settimeout.asp function TimeUtil.SetTimeout(_func, _seconds) assert(_func, '[TimeUtil] TimeUtil.SetTimeout() _func 不能为空') + assert(type(_func) == 'function', '[TimeUtil] TimeUtil.SetTimeout() _func 类型不是function') assert(_seconds >= 0, '[TimeUtil] TimeUtil.SetTimeout() 延迟时间需大于等于0') if _seconds == 0 then print('[TimeUtil] TimeUtil.SetTimeout() 事件立即执行') @@ -89,15 +94,13 @@ function TimeUtil.SetTimeout(_func, _seconds) return end local id = #eventList + 1 - -- convert to milliseconds - local ms = math.floor(_seconds * 1000) - local timestamp = ms + Timer.GetTimeMillisecond() + local timestamp = _seconds + Timer.GetTime() table.insert( eventList, { id = id, func = _func, - delay = ms, + delay = _seconds, triggerTime = timestamp } ) @@ -112,17 +115,16 @@ end -- @see https://www.w3schools.com/jsref/met_win_setinterval.asp function TimeUtil.SetInterval(_func, _seconds) assert(_func, '[TimeUtil] TimeUtil.SetInterval() _func 不能为空') + assert(type(_func) == 'function', '[TimeUtil] TimeUtil.SetInterval() _func 类型不是function') assert(_seconds > 0, '[TimeUtil] TimeUtil.SetInterval() 延迟时间需大于0') local id = #eventList + 1 - -- convert to milliseconds - local ms = math.floor(_seconds * 1000) - local timestamp = ms + Timer.GetTimeMillisecond() + local timestamp = _seconds + Timer.GetTime() table.insert( eventList, { id = id, func = _func, - delay = ms, + delay = _seconds, triggerTime = timestamp, loop = true } diff --git a/code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['UuidModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['UuidModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['UuidModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['UuidModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua similarity index 100% rename from code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua rename to Code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua diff --git a/code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua similarity index 81% rename from code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua rename to Code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua index 0fe52fb..fcf1d6c 100644 --- a/code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua @@ -3,7 +3,7 @@ --- @copyright Lilith Games, Project Da Vinci(Avatar Team) --- @see https://www.projectdavinci.com/ --- @see https://github.com/endaye/avatar-ava-xls2lua ---- source file: ./xls/ExampleTable1.xlsx +--- source file: ./Xls/ExampleTable1.xlsx local Example1Xls = { [1] = { @@ -32,15 +32,15 @@ local Example1Xls = { use_money = 123, use_food = 336.2, is_init = true, - defense = 0, + defense = nil, args_int_arr = {1, 2, 3}, args_float_arr = {1, 2.3445, 3}, args_string_arr = {'你好', '你在哪'}, args_bool_arr = {true, false}, args_vect2 = Vector2(0, 4), args_vect3 = Vector3(-2, 3, 5), - args_euler = EulerDegree(0, 0, 0), - args_color = Color(0, 0, 0, 0), + args_euler = nil, + args_color = nil, args_lua = {a = 2, b='234'}, Des1 = 'Example1_Des1_1_MMM', Des2 = 'Example1_Des2_1_MMM' @@ -58,8 +58,8 @@ local Example1Xls = { args_bool_arr = {false, true}, args_vect2 = Vector2(2, 0.5), args_vect3 = Vector3(0.6, 3, -8.4), - args_euler = EulerDegree(0, 0, 0), - args_color = Color(0, 0, 0, 0), + args_euler = nil, + args_color = nil, args_lua = nil, Des1 = 'Example1_Des1_1_ddd', Des2 = 'Example1_Des2_1_ddd' @@ -76,11 +76,11 @@ local Example1Xls = { args_int_arr = {2, 3}, args_float_arr = {200.3, 3, 234.23}, args_string_arr = {'df', 'ssd', 'dd', 'dd'}, - args_bool_arr = {}, - args_vect2 = Vector2.Zero, - args_vect3 = Vector3.Zero, - args_euler = EulerDegree(0, 0, 0), - args_color = Color(0, 0, 0, 0), + args_bool_arr = nil, + args_vect2 = nil, + args_vect3 = nil, + args_euler = nil, + args_color = nil, args_lua = nil, Des1 = 'Example1_Des1_2_farm', Des2 = 'Example1_Des2_2_farm' @@ -88,18 +88,18 @@ local Example1Xls = { MMM = { id = 2, name = 'MMM', - use_money = 0, + use_money = nil, use_food = 22.1, - is_init = false, + is_init = nil, defense = 234, args_int_arr = {3, 6, 6, 7}, args_float_arr = {3, 6.3, 6, 7}, args_string_arr = {'ss', 'd', 'd', 'd'}, args_bool_arr = {true, true}, - args_vect2 = Vector2.Zero, - args_vect3 = Vector3.Zero, - args_euler = EulerDegree(0, 0, 0), - args_color = Color(0, 0, 0, 0), + args_vect2 = nil, + args_vect3 = nil, + args_euler = nil, + args_color = nil, args_lua = "还没有添加检查", Des1 = 'Example1_Des1_2_MMM', Des2 = 'Example1_Des2_2_MMM' diff --git a/code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua similarity index 90% rename from code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua rename to Code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua index 7ac1d42..22acd09 100644 --- a/code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua @@ -3,7 +3,7 @@ --- @copyright Lilith Games, Project Da Vinci(Avatar Team) --- @see https://www.projectdavinci.com/ --- @see https://github.com/endaye/avatar-ava-xls2lua ---- source file: ./xls/ExampleTable1.xlsx +--- source file: ./Xls/ExampleTable1.xlsx local Example2Xls = { [1] = { @@ -24,7 +24,7 @@ local Example2Xls = { use_money = 123, use_food = 336.2, is_init = true, - defense = 0, + defense = nil, args1 = {1, 2, 3}, args2 = {1, 2.3445, 3}, args3 = {'你好', '你在哪'}, @@ -52,14 +52,14 @@ local Example2Xls = { args1 = {2, 3}, args2 = {200.3, 3, 234.23}, args3 = {'df', 'ssd', 'dd', 'dd'}, - args4 = {} + args4 = nil }, [5] = { id = 5, name = 'house5', - use_money = 0, + use_money = nil, use_food = 22.1, - is_init = false, + is_init = nil, defense = 234, args1 = {3, 6, 6, 7}, args2 = {3, 6.3, 6, 7}, @@ -70,11 +70,11 @@ local Example2Xls = { id = 6, name = 'horse3', use_money = 200, - use_food = 0, + use_food = nil, is_init = false, defense = 333, - args1 = {}, - args2 = {}, + args1 = nil, + args2 = nil, args3 = {'2e', 'w', 'e', 'we'}, args4 = {false, false, false, false} } diff --git a/Code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua new file mode 100644 index 0000000..a32bc49 --- /dev/null +++ b/Code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua @@ -0,0 +1,16 @@ +--- This file is generated by ava-x2l.exe, +--- Don't change it manaully. +--- @copyright Lilith Games, Project Da Vinci(Avatar Team) +--- @see https://www.projectdavinci.com/ +--- @see https://github.com/endaye/avatar-ava-xls2lua +--- source file: ./Xls/GlobalSetting.xls + +local GlobalSettingXls = { + DefaultLanguage = "CHS", + PlayerPosition = Vector3(0,-1,0), + PlayerRotation = Euler(90,0,0), + MaxPlayerNumber = 100.0, + ScoreRate = 12.5 +} + +return GlobalSettingXls diff --git a/code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua similarity index 97% rename from code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua rename to Code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua index 4bd1d8d..bceb350 100644 --- a/code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua @@ -3,7 +3,7 @@ --- @copyright Lilith Games, Project Da Vinci(Avatar Team) --- @see https://www.projectdavinci.com/ --- @see https://github.com/endaye/avatar-ava-xls2lua ---- source file: ./xls/LanguagePack.xls +--- source file: ./Xls/LanguagePack.xls local LanguagePackXls = { Example1_Des1_1_house = { diff --git a/code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua similarity index 74% rename from code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua rename to Code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua index 8718a45..424405e 100644 --- a/code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua +++ b/Code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua @@ -3,18 +3,18 @@ --- @copyright Lilith Games, Project Da Vinci(Avatar Team) --- @see https://www.projectdavinci.com/ --- @see https://github.com/endaye/avatar-ava-xls2lua ---- source file: ./xls/Sound.xls +--- source file: ./Xls/Sound.xls local SoundXls = { test_01 = { Type = 1, ID = 'test_01', - IsLoop = false, - Volume = 0, + IsLoop = nil, + Volume = nil, FileName = '', Detail = '', - Duration = 0, - CoverPlay = false + Duration = nil, + CoverPlay = nil } } diff --git a/code/['World']['S_Code']['ServerMainScript'].Script.lua b/Code/['World']['S_Code']['ServerMainScript'].Script.lua similarity index 100% rename from code/['World']['S_Code']['ServerMainScript'].Script.lua rename to Code/['World']['S_Code']['ServerMainScript'].Script.lua diff --git a/Csv/['World']['Localization']['ResourcesTable'].LocalizationResourceTable.csv b/Csv/['World']['Localization']['ResourcesTable'].LocalizationResourceTable.csv new file mode 100644 index 0000000..410feec --- /dev/null +++ b/Csv/['World']['Localization']['ResourcesTable'].LocalizationResourceTable.csv @@ -0,0 +1 @@ +LocalizeKey,Note,Default diff --git a/Csv/['World']['Localization']['TextTable'].LocalizationTextTable.csv b/Csv/['World']['Localization']['TextTable'].LocalizationTextTable.csv new file mode 100644 index 0000000..410feec --- /dev/null +++ b/Csv/['World']['Localization']['TextTable'].LocalizationTextTable.csv @@ -0,0 +1 @@ +LocalizeKey,Note,Default diff --git a/Smap/avatar-ava.smap b/Smap/avatar-ava.smap index 6beecea..9464163 100644 Binary files a/Smap/avatar-ava.smap and b/Smap/avatar-ava.smap differ diff --git a/Xls/ExampleTable1.xls b/Xls/ExampleTable1.xls new file mode 100644 index 0000000..6b02c5d --- /dev/null +++ b/Xls/ExampleTable1.xls @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97231d6bffc93b960f6faddb343335e5d34f5edab26cad8febf3134281f0fb5e +size 38912 diff --git a/xls/ExampleTable1.xlsx b/Xls/ExampleTable1.xlsx similarity index 100% rename from xls/ExampleTable1.xlsx rename to Xls/ExampleTable1.xlsx diff --git a/xls/GlobalSetting.xls b/Xls/GlobalSetting.xls similarity index 100% rename from xls/GlobalSetting.xls rename to Xls/GlobalSetting.xls diff --git a/xls/LanguagePack.xls b/Xls/LanguagePack.xls similarity index 100% rename from xls/LanguagePack.xls rename to Xls/LanguagePack.xls diff --git a/xls/Sound.xls b/Xls/Sound.xls similarity index 100% rename from xls/Sound.xls rename to Xls/Sound.xls diff --git a/code/['World']['Global']['AutoAssignTeamScript'].Script.lua b/code/['World']['Global']['AutoAssignTeamScript'].Script.lua deleted file mode 100644 index 49b8ef4..0000000 --- a/code/['World']['Global']['AutoAssignTeamScript'].Script.lua +++ /dev/null @@ -1,31 +0,0 @@ ---- 玩家加入 --- @script Auto assign --- @copyright Lilith Games, Avatar Team - ---- 编辑器默认方法 --- run once when script init -function autoAssign() - local container = world:FindTeams() - local min = 1 - local teamTojoin = {} - local playerfolder = world.Players - for i = 1, #container, 1 do - if container[i].CurrentMaxMemberNum > 0 then - temp = container[i].CurrentMemberNum / (container[i].CurrentMaxMemberNum) - if (temp < min and temp ~= 1) then - teamTojoin = {} - min = temp - table.insert(teamTojoin, container[i]) - elseif temp == min and temp ~= 1 then - table.insert(teamTojoin, container[i]) - end - end - end - local a = 1 - if #teamTojoin > 0 then - a = math.random(1, #teamTojoin) - return teamTojoin[a] - else - return nil - end -end diff --git a/code/['World']['Global']['Define']['GlobalDataModule'].ModuleScript.lua b/code/['World']['Global']['Define']['GlobalDataModule'].ModuleScript.lua deleted file mode 100644 index 2e15412..0000000 --- a/code/['World']['Global']['Define']['GlobalDataModule'].ModuleScript.lua +++ /dev/null @@ -1,9 +0,0 @@ ---- 全局变量的定义,全部定义在GlobalData这张表下面,用于全局可修改的参数 ---- @module GlobalData Defines ---- @copyright Lilith Games, Avatar Team -local GlobalData = {} - --- Test only -GlobalData.PlayerData = {} - -return GlobalData diff --git a/code/['World']['Global']['Utility']['LogUtilModule'].ModuleScript.lua b/code/['World']['Global']['Utility']['LogUtilModule'].ModuleScript.lua deleted file mode 100644 index c4de8af..0000000 --- a/code/['World']['Global']['Utility']['LogUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,45 +0,0 @@ ---- Debug工具 --- @module Debug utilities --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang -local LogUtil = {} - ---- 日志级别枚举 -LogUtil.LevelEnum = { - -- 指出细粒度信息事件对调试应用程序是非常有帮助的 主要用于开发过程中打印一些运行信息 - DEBUG = 1, - -- 消息在粗粒度级别上突出强调应用程序的运行过程 - -- 打印一些你感兴趣的或者重要的信息 这个可以用于生产环境中输出程序运行的一些重要信息 - -- 但是不能滥用 避免打印过多的日志 - INFO = 2, - -- 表明会出现潜在错误的情形 有些信息不是错误信息 但是也要给程序员的一些提示 - -- 指出虽然发生错误事件 但仍然不影响系统的继续运行 - -- 打印错误和异常信息 如果不想输出太多的日志 可以使用这个级别 - ERROR = 3 -} - ---- 日志级别 -LogUtil.level = LogUtil.LevelEnum.DEBUG - ---- 开关 -LogUtil.debugMode = true - -function LogUtil.Test(...) - if LogUtil.debugMode and LogUtil.level <= LogUtil.LevelEnum.DEBUG then - print('[TEST]', ...) - end -end - -function LogUtil.Debug(...) - if LogUtil.debugMode and LogUtil.level <= LogUtil.LevelEnum.DEBUG then - print('[DEBUG]', ...) - end -end - -function LogUtil.Info(...) - if LogUtil.debugMode and LogUtil.level <= LogUtil.LevelEnum.INFO then - print('[INFO]', ...) - end -end - -return LogUtil diff --git a/code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua b/code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua deleted file mode 100644 index 81b6e87..0000000 --- a/code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,65 +0,0 @@ ---- 音效播放模块 ----@module SoundUtil ----@copyright Lilith Games, Avatar Team ----@author Sharif Ma ----@class SoundUtil -local SoundUtil = {} - -function SoundUtil:Init() - print('[SoundUtil] Init()') - self.SoundPlaying = {} - self.Table_Sound = Config.Sound -end - ----创建一个新音效并播放 ----@param _ID number 音效的ID ----@param _SoundSourceObj Object 音效的挂载物体,不填则为2D音效,挂载在主摄像机上 -function SoundUtil:PlaySound(_ID, _SoundSourceObj) - local Info, _Duration - _SoundSourceObj = _SoundSourceObj or world.CurrentCamera - Info = self.Table_Sound[_ID] - assert(Info, '[SoundUtil] 表中不存在该ID的音效') - _Duration = Info.Duration - local sameSoundPlayingNum = 0 - for k, v in pairs(self.SoundPlaying) do - if v == _ID then - sameSoundPlayingNum = sameSoundPlayingNum + 1 - end - end - if sameSoundPlayingNum > 0 and not Info.CoverPlay then - print(string.format('[SoundUtil] %s音效CoverPlay字段为false,不能覆盖播放', _ID)) - return - end - - local Audio = world:CreateObject('AudioSource', 'Audio_' .. Info.FileName, _SoundSourceObj) - Audio.LocalPosition = Vector3.Zero - Audio.SoundClip = ResourceManager.GetSoundClip('Audio/' .. Info.FileName) - print('[SoundUtil] Audio.SoundClip', Audio.SoundClip) - Audio.Volume = Info.Volume - Audio.MaxDistance = 10 - Audio.MinDistance = 10 - Audio.Loop = Info.IsLoop - Audio:Play() - table.insert(self.SoundPlaying, _ID) - _Duration = _Duration or 1 - invoke( - function() - if Audio then - Audio:Destroy() - end - end, - _Duration - ) - invoke( - function() - for k, v in pairs(self.SoundPlaying) do - if v == _ID then - table.remove(self.SoundPlaying, k) - end - end - end, - _Duration - ) -end - -return SoundUtil diff --git a/code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua b/code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua deleted file mode 100644 index 5cb4d8d..0000000 --- a/code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua +++ /dev/null @@ -1,31 +0,0 @@ ---- This file is generated by ava-x2l.exe, ---- Don't change it manaully. ---- @copyright Lilith Games, Project Da Vinci(Avatar Team) ---- @see https://www.projectdavinci.com/ ---- @see https://github.com/endaye/avatar-ava-xls2lua ---- source file: ./xls/GlobalSetting.xls - -local GlobalSettingXls = { - DefaultLanguage = { - Key = 'DefaultLanguage', - Value = "CHS" - }, - PlayerPosition = { - Key = 'PlayerPosition', - Value = Vector3(0,-1,0) - }, - PlayerRotation = { - Key = 'PlayerRotation', - Value = Euler(90,0,0) - }, - MaxPlayerNumber = { - Key = 'MaxPlayerNumber', - Value = 100.0 - }, - ScoreRate = { - Key = 'ScoreRate', - Value = 12.5 - } -} - -return GlobalSettingXls diff --git a/xls/ExampleTable1.xls b/xls/ExampleTable1.xls deleted file mode 100644 index 8c6a4d7..0000000 --- a/xls/ExampleTable1.xls +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fae81df311873a30204372e4ec7be21c89c78e283ecf26b7d7d1bed3e3148662 -size 38912