[上一篇链接]
[游戏开发]Python打表工具系列 [第一篇][IDE开发环境部署] VSCode Python环境调试_Little丶Seven的博客-CSDN博客
[前言]
第二篇文章是对流程的概述,从第三篇开始详述打表过程每一步
[正文]
策划配表习惯使用excel,我们打表目标也是xlsm和xlsx文件
开始打表前需要确认好打表工具目录在哪,可以在所有Excel配表同层级的文件夹内新建个文件夹命名TableCreater
TableCreater里有几个目录要区分清楚,首先是python脚本文件夹叫Scripts,还有在运行工具期间生成的各种文件,用Temp文件夹存储,Temp里有PB_Python、PB_lua、Proto、Bytes等文件夹,打表流程启动后生成的各种文件要放到对应文件夹中
Scripts文件夹内要有start.py、main.py、excelToCSV.py、export_proto.py、export_pb_lua.py、export_pb_python.py、export_bytes.py、export_bytesTables.py
Scripts文件夹内的文件一看就是用来打表的各个流程代码。
虽然项目是从start.py开始启动的,但具体的打表流程我们放到main.py中运行,start.py的职责是监听用户输入信息并收集信息塞给main.py进入打表流程
如下图所示,很显然这是一个cmd文件启动了python工程,补全、提示、等都是在python代码中实现的。start.py的功能有tab键补齐名字,分号多个输入,只是输入all表示全部excel打表,以及clear清空log等操作。
如图所示,我输入了一个115_资源总表,在start.py里监听输入并自动把全路径拼齐,当按下回车时,start.py把【115_资源总表】这个名字传给main.py开始了打表流程,由于我们输入了一个表名而不是"all",因此到了main函数中自然不是全打表
import os
#读行模块
import readline
#自动补全模块
import rlcompleter
#监听用户输入模块
import userInput
import sys
#开发者自定义的系统参数
import env_debug
def getAllExportExcelNames():
inputStr = ""
isAll = False
if env_debug.isRuning:
if len(sys.argv) > 0:
inputStr,isAll = userInput.getAllExportExcelNames(sys.argv[13])
else:
print("env_debug error")
else:
#readline模块与colorama模块有冲突,无法一起使用
py = os.path.abspath("TableCreater/Python27/python")
tips = os.path.abspath("TableCreater/Script/tips.py")
os.system("{0} {1} 1".format(py,tips))
while True:
inputStr,isAll = userInput.getInput()
if inputStr == "exit":
os.system("exit")
break
elif inputStr == "clear":
os.system("cls")
py = os.path.abspath("TableCreater/Python27/python")
tips = os.path.abspath("TableCreater/Script/tips.py")
os.system("{0} {1} 1".format(py,tips))
elif inputStr == "error":
print("command error")
else:
break
return inputStr,isAll
#excelNames:要导出的所有Excel表格名称
#isAll:是否是全部导出
#isVersion:是否是出版本
def export(excelNames,isAll,isVersion=False):
import main
main.run(excelNames,isAll,isVersion)
if not isVersion:
os.system("pause")
def isVersion():
return len(sys.argv) > 0 and sys.argv[len(sys.argv) - 1] == "version"
if __name__ == '__main__':
env_debug.switch("common")
caller = sys.argv[1]
if caller == "1": #常规导表
if not isVersion():#显示用户输入
excelNames,isAll = getAllExportExcelNames()
if excelNames != "exit":
export(excelNames,isAll)
else: #直接导出所有
excelNames,isAll = userInput.getAllExportExcelNames("all")
export(excelNames,isAll,True)
elif caller == "2": #C#导表
excelNames,isAll = userInput.getAllExportExcelNames(sys.argv[10])
export(excelNames,True,isVersion())
elif caller == "3": #本地战斗服务器专属导表
excelNames,isAll = userInput.getAllExportExcelNames(sys.argv[8])
export(excelNames,True,isVersion())
最上面有个 import env_debug模块,这是我们自己写的系统变量定义文件 env_debug.py文件里,这里有必要解释一下为何存在一个系统变量文件。我们的打表工具将来是要暴露给所有开发人员使用的,因此需要有一个快捷入口,而一个功能健全的打表工具很显然不能只有单一的打表功能,还要支持定制化内容,下面是我项目打表工具的启动入口 run.cmd,该文件就是python工程的入口,并向python工程传入各种参数参数,看一下该cmd文件的callType,很明显该工程支持生成多类型。可以生成lua读表、C#读表功能以及对战斗服务做了支持。启动python工程的代码后面带了茫茫多的参数, env_debug.py文件里就是存了这些参数,方便开发者调试用的。
@echo off
REM ***isVersion代表是否为出版本<做为最后一个参数传入,默认:空,不是出版本,如果该值为version代表出版本>***
call export_setting.bat
set callType=1
REM 重载
set callType=%1
if "%callType%"=="1" goto export_common
if "%callType%"=="2" goto export_csharp
if "%callType%"=="3" goto export_battle
REM 常规导出
:export_common
set isVersion=%2
"./TableCreater/Python27/python" "./TableCreater/Script/start.py" 1 %import_excel_path% %import_enum_path% %export_public_path% %export_client_lua_pb% %export_client_lua_script% %export_client_byte% %export_server_proto% %export_server_byte% %export_client_project_lua_pb% %export_client_project_lua_script% %export_client_project_byte% %isVersion%
goto end
REM 导出C#版
:export_csharp
set isVersion=%4
"./TableCreater/Python27/python" "./TableCreater/Script/start.py" 2 %import_excel_path% %import_enum_path% %export_public_path% %export_client_cs_pb% %export_client_cs_script% %export_client_project_cs_pb% %export_client_project_cs_script% %2 %3 %isVersion%
goto end
REM 导出本地战斗版
:export_battle
set isVersion=%6
"./TableCreater/Python27/python" "./TableCreater/Script/start.py" 3 %import_excel_path% %import_enum_path% %export_public_path% %2 %3 %4 %5 %isVersion%
:end
接下来是env_debug.py的代码,14个参数很明显,就是上面这个run.cmd文件传入的各个参数。env_debug.py存在的意义就是工具开发人员不需要用run.cmd文件传入参数,而是本地已经模拟好了这些参数。直接启动python工程调试即可。
#客户端测试环境
def enter_debug_env_common():
sys.argv = [0]*14
sys.argv[0] = "./TableCreater/Script/start.py"
sys.argv[1] = "1"
sys.argv[2] = "./"
sys.argv[3] = "./../EnumData"
sys.argv[4] = "./../AllData"
sys.argv[5] = "./../AllData/client/lua_pb"
sys.argv[6] = "./../AllData/client/lua_script"
sys.argv[7] = "./../AllData/client/bytes"
sys.argv[8] = "./../AllData/server/proto"
sys.argv[9] = "./../AllData/server/bytes"
sys.argv[10] = "./../../Client-Full/Assets/Lua/Logic/Table/AutoGen/TablePb"
sys.argv[11] = "./../../Client-Full/Assets/Lua/Logic/Table/AutoGen"
sys.argv[12] = "./../../Client-Full/Assets/Res/Data/Tables"
sys.argv[13] = "152_掉落配置表.xlsx"
to_gbk()
print(">>>>进入测试环境:[common]<<<<".decode('utf-8').encode('gbk'))
#选择环境
#default_env默认环境 common
def switch(default_env = "common"):
if (len(sys.argv)>2):#外部调用
return
global isRuning
isRuning = True
if default_env == "common":
enter_debug_env_common()
elif default_env == "csharp":
enter_debug_env_csharp()
elif default_env == "battle":
enter_debug_env_battle()
[main.py代码]
下面介绍一下main.py具体代码与负责内容,先上代码
#! python2
# coding=utf-8
import traceback
import os
import time
import sys
import tools
import setting
import debug
import Parser
import env
import math
import tips
import export_csv
import export_merge
import export_proto
import export_code
import export_pb_lua
import export_pb_python
import export_pb_csharp
import export_pb_csharp_enums
import export_byte
import export_clientcode
import export_clientscript_csharp
import checkAll
from tableData import tableManager
# import chardet
def execute_csv(excelNames):
for i in range(len(excelNames)):
debug.log("\n开始分析{}信息...\n".format(debug.toUTF8(excelNames[i])))
# 生成csv
export_csv.execute(excelNames[i])
def async_execute_csv(excelNames):
tools.async_process(excelNames, execute_csv)
merge_csvs = tableManager.getNeedMergeTable()
if(len(merge_csvs) > 0):
# 合并csv
debug.log("\n开始合并csv=>")
export_merge.execute(merge_csvs)
debug.log("\n合并完毕!")
# 常规导表
def export_common(excelNames, isVersion):
Parser.curToolUser = "1"
#生成csv
async_execute_csv(excelNames)
# 清空临时文件
tools.clearDir(setting.dirPath_temp)
csvs_client, filts_server = tableManager.getTableNames(["common", "client"])
csvs_server, filts_client = tableManager.getTableNames(["common", "server"])
if len(csvs_server) == 0:
debug.warning("\n分析结果:输入源为客户端专属表格!")
if len(csvs_client) == 0:
debug.warning("\n分析结果:输入源为服务器专属表格!")
if len(csvs_client) > 0:
Parser.curToolUser = "2"
debug.log("\n开始生成客户端配置文件=>")
debug.log(csvs_client)
if len(filts_server) > 0:
debug.log("\n过滤服务器专属表格=>")
debug.warning(filts_server)
# 生成proto文件
debug.log("\n开始生成客户端proto...\n")
export_proto.execute(csvs_client)
# 生成proto对应的python解析脚本
debug.log("开始生成客户端proto对应的python版pb脚本...\n")
export_pb_python.execute(csvs_client)
# 生成proto对应的lua解析脚本
debug.log("开始生成客户端proto对应的lua版pb脚本...\n")
export_pb_lua.execute(csvs_client)
# 生成导出bytes代码
debug.log("开始生成客户端导出bytes代码...\n")
export_code.execute(csvs_client)
# 生成bytes
debug.log("开始生成客户端bytes...")
export_byte.execute(csvs_client)
# 生成客户端使用代码
debug.log("\n开始生成客户端相关代码...\n")
export_clientcode.execute()
# 拷贝pb文件与bytes到项目
debug.log("拷贝pb文件、bytes、相关代码 到AllData...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_pb_lua), env.export_client_lua_pb)
tools.copyFiles(os.path.abspath(setting.protocPath_clientScript), env.export_client_lua_script)
tools.copyFiles(os.path.abspath(setting.dirPath_byte), env.export_client_byte)
tools.copyFiles(os.path.abspath(setting.dirPath_byte_clientOnly), env.export_client_byte+"/clientOnly/")
if not isVersion: # 出版本不拷贝到项目
debug.log("拷贝pb文件、bytes、相关代码 到项目...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_pb_lua), env.export_client_project_lua_pb)
tools.copyFiles(os.path.abspath(setting.protocPath_clientScript), env.export_client_project_lua_script)
tools.copyFiles(os.path.abspath(setting.dirPath_byte), env.export_client_project_byte)
tools.copyFiles(os.path.abspath(setting.dirPath_byte_clientOnly), env.export_client_project_byte+"/ClientOnly/")
debug.log("生成客户端配置完毕!\n")
if len(csvs_server) > 0:
# 清空临时文件
tools.clearDir(setting.dirPath_temp)
Parser.curToolUser = "3"
debug.log("开始生成服务器配置文件=>")
debug.log(csvs_server)
if len(filts_client) > 0:
debug.log("\n过滤客户端专属表格=>")
debug.warning(filts_client)
#生成proto文件
debug.log("\n开始生成服务器proto...\n")
export_proto.execute(csvs_server)
#生成proto对应的python解析脚本
debug.log("开始生成服务器proto对应的python版pb脚本...\n")
export_pb_python.execute(csvs_server)
#生成导出bytes代码
debug.log("开始生成服务器导出bytes代码...\n")
export_code.execute(csvs_server)
#生成bytes
debug.log("开始生成服务器bytes...")
export_byte.execute(csvs_server)
#拷贝pb文件与bytes到AllData
debug.log("\n拷贝服务器proto文件、bytes、到AllData...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_proto),env.export_server_proto)
tools.copyFiles(os.path.abspath(setting.dirPath_byte),env.export_server_byte)
debug.log("服务器配置生成完毕!")
#客户端C#导表
def export_csharp(excelNames,isVersion):
Parser.curToolUser = "1"
async_execute_csv(excelNames)
# 清空临时文件
tools.clearDir(setting.dirPath_temp)
# debug.log("\n开始生成客户端C#版枚举代码...")
# enumProtoPath = env.import_enum_path + "/proto"
# enumProtos = sys.argv[9] #传入的枚举
# #生成C#版枚举
# export_pb_csharp_enums.execute(enumProtoPath,enumProtos)
debug.log("\n开始生成客户端C#版配表相关代码...")
Parser.curToolUser = "2"
csvs_client, filts_server = tableManager.getTableNames(["common", "client"])
if len(filts_server) > 0:
debug.log("\n过滤服务器专属表格=>")
debug.warning(filts_server)
if len(csvs_client) == 0:
debug.warning("\n分析结果:输入源为服务器专属表格!")
else:
debug.log("\n开始生成客户端C#版proto...")
export_proto.execute(csvs_client)
debug.log("\n开始生成客户端C#版proto对应的pb脚本...")
export_pb_csharp.execute(csvs_client)
allTables = tableManager.getAllTableNames()
debug.log("\n开始生成成客户端C#版AllTable.cs...")
export_clientscript_csharp.execute(allTables)
#拷贝pb文件与bytes到AllData
debug.log("\n拷贝客户端C#版pb文件、相关代码 到AllData...")
tools.copyFiles(os.path.abspath(setting.dirPath_pb_csharp),env.export_client_cs_pb)
tools.copyFiles(os.path.abspath(setting.protocPath_clientScript_csharp),env.export_client_cs_script)
if not isVersion:
#拷贝pb文件与bytes到项目
debug.log("\n拷贝客户端C#版pb文件、相关代码 到项目...")
tools.copyFiles(os.path.abspath(setting.dirPath_pb_csharp),env.export_client_project_cs_pb)
tools.copyFiles(os.path.abspath(setting.protocPath_clientScript_csharp),env.export_client_project_cs_script)
debug.log("\n生成完毕...")
#战斗导表
def export_battle(excelNames,isVersion):
Parser.curToolUser = "1"
async_execute_csv(excelNames)
#清空临时文件
tools.clearDir(setting.dirPath_temp)
csvs_battle = []
#战斗相关的配置必须是common,因为战斗服务器在客户端跑
allTables = tableManager.getAllTableData()
for key, table in allTables.items():
if table.tableUser != "common":
debug.throwError("配表{}归属者不是common!".format(table.name))
csvs_battle.append(table.name)
if len(csvs_battle) > 0:
Parser.curToolUser = "2"
debug.log("\n开始生成战斗相关配置文件=>")
debug.log(csvs_battle)
#生成proto文件
debug.log("\n开始生成战斗相关配置proto...\n")
export_proto.execute(csvs_battle)
#生成proto对应的python解析脚本
debug.log("开始生成战斗相关配置proto对应的python版pb脚本...\n")
export_pb_python.execute(csvs_battle)
#生成proto对应的lua解析脚本
debug.log("开始生成战斗相关配置proto对应的lua版pb脚本...\n")
export_pb_lua.execute(csvs_battle)
#生成导出bytes代码
debug.log("开始生成战斗相关配置的bytes导出代码...\n")
export_code.execute(csvs_battle)
#生成bytes
debug.log("开始生成战斗相关配置bytes...\n")
export_byte.execute(csvs_battle)
#拷贝pb文件与bytes到项目
debug.log("开始拷贝战斗相关配置 proto文件、pb文件、bytes 到 指定路径...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_proto),env.export_battle_proto)
tools.copyFiles(os.path.abspath(setting.dirPath_pb_lua),env.export_battle_lua_pb)
tools.copyFiles(os.path.abspath(setting.dirPath_byte),env.export_battle_byte)
debug.log("生成战斗相关配置完毕!\n")
#检查重复
def export_check(excelNames):
Parser.curToolUser = "1"
for i in range(len(excelNames)):
debug.log("\n开始分析{}信息...\n".format(debug.toUTF8(excelNames[i])))
# 生成csv
checkAll.checkExcel(excelNames[i])
# 入口函数
def run(excelNames, isAll, isVersion):
try:
# 初始化环境变量
env.init()
beginTime = time.time()
caller = sys.argv[1]
# 准备导出环境
env.prepare(isAll, isVersion)
if caller == "1": #常规导表
export_common(excelNames,isVersion)
elif caller == "2": #导客户端C#表
export_csharp(excelNames,isVersion)
elif caller == "3": #导战斗表
export_battle(excelNames,isVersion)
#清空临时文件
tools.clearDir(setting.dirPath_temp)
#显示耗时
userTime = int(time.time() - beginTime + 0.5)
time_min = userTime//60
if time_min > 0:
debug.greenlog("\n操作完成,总耗时{}分{}秒\n".format(time_min,userTime%60))
else:
debug.greenlog("\n操作完成,总耗时{}秒\n".format(userTime))
except Exception,err:
debug.touchError(err)
#只做数据检查,并不导出任何文件
def runCheckOnly(excelNames, isAll):
try:
beginTime = time.time()
caller = sys.argv[1]
# 准备全部ID数据
env.checkPrepare()
#开始检查
export_check(excelNames)
#显示耗时
userTime = int(time.time() - beginTime + 0.5)
time_min = userTime//60
if time_min > 0:
debug.greenlog("\n操作完成,总耗时{}分{}秒\n".format(time_min,userTime%60))
else:
debug.greenlog("\n操作完成,总耗时{}秒\n".format(userTime))
except Exception,err:
debug.touchError(err)
main.py代码分为两部分,第一部分是run函数,start.py调用该函数启动打表流程,run函数也很简单,就是区分打表类型。具体的打表流程在export_common函数中。
下面重点介绍一下export_common函数,该函数内调用的各个步骤接口,就是本系列教程的核心代码,在导表的过过程中还加入了客户端表和服务器表的数据过滤,可以看到服务器、客户端判断的相关代码。
# 常规导表
def export_common(excelNames, isVersion):
Parser.curToolUser = "1"
#生成csv
async_execute_csv(excelNames)
# 清空临时文件
tools.clearDir(setting.dirPath_temp)
csvs_client, filts_server = tableManager.getTableNames(["common", "client"])
csvs_server, filts_client = tableManager.getTableNames(["common", "server"])
if len(csvs_server) == 0:
debug.warning("\n分析结果:输入源为客户端专属表格!")
if len(csvs_client) == 0:
debug.warning("\n分析结果:输入源为服务器专属表格!")
if len(csvs_client) > 0:
Parser.curToolUser = "2"
debug.log("\n开始生成客户端配置文件=>")
debug.log(csvs_client)
if len(filts_server) > 0:
debug.log("\n过滤服务器专属表格=>")
debug.warning(filts_server)
# 生成proto文件
debug.log("\n开始生成客户端proto...\n")
export_proto.execute(csvs_client)
# 生成proto对应的python解析脚本
debug.log("开始生成客户端proto对应的python版pb脚本...\n")
export_pb_python.execute(csvs_client)
# 生成proto对应的lua解析脚本
debug.log("开始生成客户端proto对应的lua版pb脚本...\n")
export_pb_lua.execute(csvs_client)
# 生成导出bytes代码
debug.log("开始生成客户端导出bytes代码...\n")
export_code.execute(csvs_client)
# 生成bytes
debug.log("开始生成客户端bytes...")
export_byte.execute(csvs_client)
# 生成客户端使用代码
debug.log("\n开始生成客户端相关代码...\n")
export_clientcode.execute()
# 拷贝pb文件与bytes到项目
debug.log("拷贝pb文件、bytes、相关代码 到AllData...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_pb_lua), env.export_client_lua_pb)
tools.copyFiles(os.path.abspath(setting.protocPath_clientScript), env.export_client_lua_script)
tools.copyFiles(os.path.abspath(setting.dirPath_byte), env.export_client_byte)
tools.copyFiles(os.path.abspath(setting.dirPath_byte_clientOnly), env.export_client_byte+"/clientOnly/")
if not isVersion: # 出版本不拷贝到项目
debug.log("拷贝pb文件、bytes、相关代码 到项目...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_pb_lua), env.export_client_project_lua_pb)
tools.copyFiles(os.path.abspath(setting.protocPath_clientScript), env.export_client_project_lua_script)
tools.copyFiles(os.path.abspath(setting.dirPath_byte), env.export_client_project_byte)
tools.copyFiles(os.path.abspath(setting.dirPath_byte_clientOnly), env.export_client_project_byte+"/ClientOnly/")
debug.log("生成客户端配置完毕!\n")
if len(csvs_server) > 0:
# 清空临时文件
tools.clearDir(setting.dirPath_temp)
Parser.curToolUser = "3"
debug.log("开始生成服务器配置文件=>")
debug.log(csvs_server)
if len(filts_client) > 0:
debug.log("\n过滤客户端专属表格=>")
debug.warning(filts_client)
#生成proto文件
debug.log("\n开始生成服务器proto...\n")
export_proto.execute(csvs_server)
#生成proto对应的python解析脚本
debug.log("开始生成服务器proto对应的python版pb脚本...\n")
export_pb_python.execute(csvs_server)
#生成导出bytes代码
debug.log("开始生成服务器导出bytes代码...\n")
export_code.execute(csvs_server)
#生成bytes
debug.log("开始生成服务器bytes...")
export_byte.execute(csvs_server)
#拷贝pb文件与bytes到AllData
debug.log("\n拷贝服务器proto文件、bytes、到AllData...\n")
tools.copyFiles(os.path.abspath(setting.dirPath_proto),env.export_server_proto)
tools.copyFiles(os.path.abspath(setting.dirPath_byte),env.export_server_byte)
debug.log("服务器配置生成完毕!")
[开始简述打表过程啦!!!]
[第一步]将excel文件转CSV并输出到CSV目录
https://blog.csdn.net/liuyongjie1992/article/details/117324693
[第二步]读取csv的数据类型行和数据名称行,使用这两行数据生成proto
https://blog.csdn.net/liuyongjie1992/article/details/117324749
[第三步]调用protoc-gen-lua.bat生成lua版pb文件
https://blog.csdn.net/liuyongjie1992/article/details/117324829
[第四步]调用protoc.exe生成python版pb文件
https://blog.csdn.net/liuyongjie1992/article/details/117325176
[第五步]csv生成bytes文件导入Unity工程等待读数据
https://blog.csdn.net/liuyongjie1992/article/details/117325209
[第六步]生成Lua读表脚本,业务开发调用该脚本读数据
https://blog.csdn.net/liuyongjie1992/article/details/117325259
[第七步]生成统一的读表接口
https://blog.csdn.net/liuyongjie1992/article/details/117325385
[第八步]业务层使用读表功能
https://blog.csdn.net/liuyongjie1992/article/details/117571169
额外小知识与完结语
https://blog.csdn.net/liuyongjie1992/article/details/117325408
[下一篇链接]
https://blog.csdn.net/liuyongjie1992/article/details/117324693