mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 16:25:36 +00:00
Compare commits
24 Commits
bf0a3f3feb
...
ddcfa56467
Author | SHA1 | Date | |
---|---|---|---|
ddcfa56467 | |||
9e1017609c | |||
d76262fc43 | |||
9253f24421 | |||
8435b00617 | |||
847fe68417 | |||
5f32bf8423 | |||
139143c517 | |||
4bd2e28a21 | |||
28203c5836 | |||
7de5837db4 | |||
cb00829ae7 | |||
59b0e58801 | |||
5b814efbb9 | |||
17b8be1b9d | |||
25ac16c300 | |||
1fac1d5ae9 | |||
d6021ddc2d | |||
8e8c66c3bf | |||
4273571d97 | |||
71df291391 | |||
cf0f397465 | |||
7b579d2916 | |||
a3386339d0 |
@ -55,8 +55,8 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
- [ ] Python part:
|
- [ ] Python part:
|
||||||
- [x] Load Python plugins
|
- [x] Load Python plugins
|
||||||
- [x] Async support
|
- [x] Async support
|
||||||
- [ ] KuiToi class
|
- [x] KuiToi class
|
||||||
- [ ] Client (Player) class
|
- [x] Client (Player) class
|
||||||
- [ ] JavaScript part:
|
- [ ] JavaScript part:
|
||||||
- [ ] Load JavaScript plugins
|
- [ ] Load JavaScript plugins
|
||||||
- [ ] KuiToi class
|
- [ ] KuiToi class
|
||||||
|
48
docs/cn/multilanguage/example.json
Normal file
48
docs/cn/multilanguage/example.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"": "基础阶段",
|
||||||
|
"hello": "来自KuiToi服务器的问候!",
|
||||||
|
"config_path": "使用{}进行配置。",
|
||||||
|
"init_ok": "初始化完成。",
|
||||||
|
"start": "服务器已启动!",
|
||||||
|
"stop": "服务器已停止!",
|
||||||
|
|
||||||
|
"": "服务器认证",
|
||||||
|
"auth_need_key": "需要BeamMP密钥才能启动!",
|
||||||
|
"auth_empty_key": "BeamMP密钥为空!",
|
||||||
|
"auth_cannot_open_browser": "无法打开浏览器:{}",
|
||||||
|
"auth_use_link": "使用此链接:{}",
|
||||||
|
|
||||||
|
"": "GUI阶段",
|
||||||
|
"GUI_yes": "是",
|
||||||
|
"GUI_no": "否",
|
||||||
|
"GUI_ok": "确定",
|
||||||
|
"GUI_cancel": "取消",
|
||||||
|
"GUI_need_key_message": "需要BeamMP密钥才能启动!\n是否在浏览器中打开链接以获取密钥?",
|
||||||
|
"GUI_enter_key_message": "请输入密钥:",
|
||||||
|
"GUI_cannot_open_browser": "无法打开浏览器。\n请使用此链接:{}",
|
||||||
|
|
||||||
|
"": "Web阶段",
|
||||||
|
"web_start": "WebAPI已启动{}(CTRL+C停止)",
|
||||||
|
|
||||||
|
"": "命令:man",
|
||||||
|
"man_message_man": "man - 显示COMMAND的帮助页面。\n用法:man COMMAND",
|
||||||
|
"help_message_man": "显示COMMAND的帮助页面。",
|
||||||
|
"man_for": "帮助页面",
|
||||||
|
"man_message_not_found": "man:找不到帮助页面。",
|
||||||
|
"man_command_not_found": "man:找不到\"{}\"命令!",
|
||||||
|
|
||||||
|
"": "命令:help",
|
||||||
|
"man_message_help": "help - 显示命令的名称和简短描述。\n用法:help [--raw]\n命令`help`列出所有可用的命令,并为每个命令提供简短描述。",
|
||||||
|
"help_message_help": "显示命令的名称和简短描述。",
|
||||||
|
"help_command": "命令",
|
||||||
|
"help_message": "文本",
|
||||||
|
"help_message_not_found": "无文本",
|
||||||
|
|
||||||
|
"": "命令:stop",
|
||||||
|
"man_message_stop": "stop - 关闭服务器。\n用法:stop",
|
||||||
|
"help_message_stop": "关闭服务器。",
|
||||||
|
|
||||||
|
"": "命令:exit",
|
||||||
|
"man_message_exit": "exit - 关闭服务器。\n用法:exit",
|
||||||
|
"help_message_exit": "关闭服务器。"
|
||||||
|
}
|
3
docs/cn/multilanguage/readme.md
Normal file
3
docs/cn/multilanguage/readme.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# MultiLanguage - i18n支持
|
||||||
|
|
||||||
|
在 [example.json](./example.json) 中是 [src/modules/i18n/files/ru.json](../../../src/modules/i18n/files/ru.json) 的副本。如果你想将其翻译成以前未翻译过的语言,或者更新现有的翻译,我将很高兴接受你的拉取请求。
|
174
docs/cn/plugins/classes.md
Normal file
174
docs/cn/plugins/classes.md
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
# 传递的类
|
||||||
|
|
||||||
|
## 值得一读
|
||||||
|
|
||||||
|
1. 什么是“*args”和“**kwargs”? -> [habr上的文章 ↗](https://habr.com/ru/companies/ruvds/articles/482464/)
|
||||||
|
|
||||||
|
## KuiToi
|
||||||
|
_`kt = KuiToi("PluginName"")`_
|
||||||
|
|
||||||
|
### kt.log
|
||||||
|
_常量_\
|
||||||
|
返回预配置的记录器
|
||||||
|
|
||||||
|
### kt.name
|
||||||
|
_常量_\
|
||||||
|
返回插件名称
|
||||||
|
|
||||||
|
### kt.dir
|
||||||
|
_常量_\
|
||||||
|
返回插件文件夹
|
||||||
|
|
||||||
|
### kt.open()
|
||||||
|
_与open()参数相同_\
|
||||||
|
在kt.dir中打开文件
|
||||||
|
|
||||||
|
### kt.register_event(event_name: str, event_func: function)
|
||||||
|
_`event_name: str` -> 作为`event_func`调用的事件名称._\
|
||||||
|
_`event_func: function` -> 要调用的函数._
|
||||||
|
|
||||||
|
在`event_func`中,可以传递普通函数或async - 不需要提前进行await。\
|
||||||
|
您也可以创建自己的事件,并使用自己的名称注册任意数量的事件。
|
||||||
|
|
||||||
|
### kt.call_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> 要调用的事件名称._\
|
||||||
|
_`*args, **kwargs` -> 要传递给函数的参数._
|
||||||
|
|
||||||
|
### **async** kt.call_async_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> 要调用的事件名称._\
|
||||||
|
_`*args, **kwargs` -> 要传递给函数的参数._\
|
||||||
|
_需要用`await`调用_
|
||||||
|
|
||||||
|
###### _建议阅读*args, **kwargs,链接在开头_
|
||||||
|
所有事件的数据都以以下格式传递:`{"event_name": event_name, "args": args, "kwargs": kwargs}`\
|
||||||
|
`args: list` -> 表示传递到事件中的数据数组\
|
||||||
|
`kwargs: dict` -> 表示传递到事件中的数据字典
|
||||||
|
数据将以数组形式从所有成功的波动中返回。
|
||||||
|
|
||||||
|
### kt.call_lua_event(event_name: str, *args) -> list:
|
||||||
|
_`event_name: str` -> 要调用的事件名称._\
|
||||||
|
_`*args` -> 要传递给函数的参数._
|
||||||
|
|
||||||
|
添加用于向后兼容性。\
|
||||||
|
lua函数使用直接传递参数`lua_func(*args)`进行调用。
|
||||||
|
|
||||||
|
### kt.get_player([pid: int], [nick: str]) -> Player | None:
|
||||||
|
_`pid: int` -> Player ID - 玩家标识符._\
|
||||||
|
_`nick: str` -> Player Nick - 玩家昵称._
|
||||||
|
|
||||||
|
该方法通过其`pid`或`nick`返回玩家对象。\
|
||||||
|
如果无法找到玩家,则返回 `None`。
|
||||||
|
|
||||||
|
### kt.get_players() -> List[Player] | list:
|
||||||
|
|
||||||
|
该方法返回所有玩家的数组。\
|
||||||
|
如果没有玩家,则数组将为空。
|
||||||
|
|
||||||
|
### kt.players_counter() -> int:
|
||||||
|
|
||||||
|
该方法返回在线的玩家数量。
|
||||||
|
|
||||||
|
### kt.is_player_connected([pid: int], [nick: str]) -> bool:
|
||||||
|
_`pid: int` -> Player ID - 玩家标识符._\
|
||||||
|
_`nick: str` -> Player Nick - 玩家昵称._
|
||||||
|
|
||||||
|
该方法通过其`pid`或`nick`返回玩家对象。
|
||||||
|
|
||||||
|
## Player (或 Client)
|
||||||
|
_`pl = kt.get_player()`_\
|
||||||
|
_`pl = event_data['kwargs']['player']`_
|
||||||
|
|
||||||
|
### pl.log -> Logger
|
||||||
|
_常量_\
|
||||||
|
返回预配置的记录器
|
||||||
|
|
||||||
|
### pl.addr -> str
|
||||||
|
_常量_\
|
||||||
|
返回玩家的 IP 地址
|
||||||
|
|
||||||
|
### pl.pid -> int
|
||||||
|
### pl.cid -> int
|
||||||
|
_常量_\
|
||||||
|
返回客户端的 ID _(pid: PlayerId = cid: ClientId)_
|
||||||
|
|
||||||
|
### pl.key -> str
|
||||||
|
_常量_\
|
||||||
|
返回在身份验证期间传递的密钥
|
||||||
|
|
||||||
|
### pl.nick -> str
|
||||||
|
_变量_\
|
||||||
|
从 BeamMP 服务器传递的昵称,可以更改,后果未知
|
||||||
|
|
||||||
|
### pl.roles -> str
|
||||||
|
_变量_\
|
||||||
|
从 BeamMP 服务器传递的角色,可以更改(如果设置了不正确的角色,可能会发生意外情况。)
|
||||||
|
|
||||||
|
### pl.guest -> bool
|
||||||
|
_常量_\
|
||||||
|
返回玩家是否为游客,从 BeamMP 服务器传递
|
||||||
|
|
||||||
|
### pl.identifiers -> dict
|
||||||
|
_常量_\
|
||||||
|
标识符,从 BeamMP 服务器传递。
|
||||||
|
|
||||||
|
### pl.ready -> bool
|
||||||
|
_常量,由核心更改_\
|
||||||
|
返回布尔值,如果为 True-> 玩家已下载所有资源,在地图上加载
|
||||||
|
|
||||||
|
### pl.cars -> dict
|
||||||
|
_常量,由核心更改_\
|
||||||
|
按类型返回汽车字典:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
1: {
|
||||||
|
"packet": car_packet,
|
||||||
|
"json": car_json,
|
||||||
|
"json_ok": bool(car_json),
|
||||||
|
"snowman": snowman,
|
||||||
|
"over_spawn": (snowman and allow_snowman) or over_spawn,
|
||||||
|
"pos": {
|
||||||
|
"pos":[0,0,0],
|
||||||
|
"rvel":[0,0,0],
|
||||||
|
"rot":[0,0,0],
|
||||||
|
"vel":[0,0,0],
|
||||||
|
"tim":0,
|
||||||
|
"ping":0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2: ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
其中 `1` - car_id\
|
||||||
|
其中 `pkt` - 未处理的从客户端收到的数据包(仅供非常有经验的用户使用)\
|
||||||
|
其中 `json` - 以 dict 形式存储的已处理的数据包\
|
||||||
|
其中 `json_ok` - 核心是否能够处理数据包\
|
||||||
|
其中 `snowman` - 车辆是否为雪人\
|
||||||
|
其中 `over_spawn` - 车辆是否超过了生成限制(通过插件允许)\
|
||||||
|
其中 `pos` - 车辆位置(通过 UDP 传递)
|
||||||
|
|
||||||
|
### pl.last_position -> dict
|
||||||
|
_常量,由核心更改_
|
||||||
|
返回玩家的最后位置
|
||||||
|
|
||||||
|
|
||||||
|
### **async** pl.kick([reason: str = "Kicked!"]) -> None
|
||||||
|
_`reason: str` -> 踢出理由,参数可选,默认值为 `Kicked!`_
|
||||||
|
将玩家踢出服务器
|
||||||
|
|
||||||
|
### **async** pl.send_message(message: str, [to_all: bool = True]) -> None
|
||||||
|
_`message: str` -> 消息文本,不带 "Server:"_
|
||||||
|
_`to_all: bool` -> 是否向所有人发送此消息?参数可选,默认值为 `True`_
|
||||||
|
向玩家或所有人发送消息
|
||||||
|
|
||||||
|
### **async** pl.send_event(event_name: str, event_data: Any, [to_all: bool = True]) -> None
|
||||||
|
_`event_name: str` -> 要调用的事件名称_
|
||||||
|
_`event_data: Any` -> 发送到事件的数据。_
|
||||||
|
_`to_all: bool` -> 是否向所有人发送此消息?参数可选,默认值为 `True`_
|
||||||
|
将事件发送到客户端。\
|
||||||
|
如果 event_data 是 tuple、list、dict,则核心会通过 json.dumps(event_data) 将其转换为 json,然后再发送。\
|
||||||
|
否则,数据将是字符串,不受限制;
|
||||||
|
|
||||||
|
### **async** pl.delete_car(self, car_id: int) -> None
|
||||||
|
_`car_id: int` -> 要删除的车辆的 ID_
|
||||||
|
删除玩家的车辆
|
3
docs/cn/plugins/events_list.md
Normal file
3
docs/cn/plugins/events_list.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# 所有可用事件列表
|
||||||
|
|
||||||
|
大多数事件将包含`pl = data ['kwargs'] ['player']`,可以在[这里](./classes.md)找到描述。
|
27
docs/cn/plugins/lua/example.lua
Normal file
27
docs/cn/plugins/lua/example.lua
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
print("example.lua")
|
||||||
|
|
||||||
|
--CreateTimer Testing
|
||||||
|
local mytimer = MP.CreateTimer()
|
||||||
|
--.--.--.--.--.--.--.
|
||||||
|
|
||||||
|
--GetOSName Testing
|
||||||
|
print("OS Name: "..MP.GetOSName())
|
||||||
|
--.--.--.--.--.--.-
|
||||||
|
|
||||||
|
--GetServerVersion Testing
|
||||||
|
local major, minor, patch = MP.GetServerVersion()
|
||||||
|
print("Server Version: "..major.."."..minor.."."..patch)
|
||||||
|
--.--.--.--.--.--.--.--.--
|
||||||
|
|
||||||
|
--Events Testing--
|
||||||
|
function handleChat(player_id, player_name, message)
|
||||||
|
print("Lua handleChat:", player_id, player_name, message, "; Uptime: "..mytimer:GetCurrent())
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
MP.RegisterEvent("onChatMessage", "handleChat")
|
||||||
|
--.--.--.--.--.--.
|
||||||
|
|
||||||
|
function onInit()
|
||||||
|
print("Initializing ready!")
|
||||||
|
end
|
35
docs/cn/plugins/lua/readme.md
Normal file
35
docs/cn/plugins/lua/readme.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# BeamMP Lua反馈支持
|
||||||
|
|
||||||
|
KiuToi几乎完全支持BeamMP的lua插件,所有必要的方法都已经创建,测试显示以下细节:
|
||||||
|
|
||||||
|
在KiuToi中没有支持:`MP.Set()`
|
||||||
|
|
||||||
|
#### Economic Rework V2.0(付费,Discord(RU):[Hlebushek](https://discordapp.com/users/449634697593749516))
|
||||||
|
|
||||||
|
1. 要获取`pluginPath`,需要:`debug.getinfo(1).source:gsub("\\","/")` => `debug.getinfo(1).source:gsub("\\","/"):gsub("@", "")`,因为路径返回值中包含`@`,这破坏了插件。
|
||||||
|
|
||||||
|
#### Cobalt Essentials V1.7.5(免费,[github ↗](https://github.com/prestonelam2003/CobaltEssentials/))
|
||||||
|
|
||||||
|
1. 要获取`pluginPath`,需要:`debug.getinfo(1).source:gsub("\\","/")` => `debug.getinfo(1).source:gsub("\\","/"):gsub("@", "")`,因为路径返回值中包含`@`,这破坏了插件。
|
||||||
|
2. 必须将所有的`require()`移动到`onInit`之后。
|
||||||
|
3. 在某些情况下,必须在函数声明之后注册`MP.RegisterEvent`,即:
|
||||||
|
```lua
|
||||||
|
--这样不正确,可能无法注册
|
||||||
|
MP.RegisterEvent("onPlayerAuth","onPlayerAuth")
|
||||||
|
function onPlayerAuth(name, role, isGuest)
|
||||||
|
-- Some plugin code
|
||||||
|
end
|
||||||
|
|
||||||
|
--这样就可以了
|
||||||
|
MP.RegisterEvent("onPlayerAuth","onPlayerAuth")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 工作原理
|
||||||
|
|
||||||
|
插件加载经过几个阶段:
|
||||||
|
|
||||||
|
1. 扫描`plugins/`文件夹
|
||||||
|
2. 如果文件夹不在PyPlugins中,并且文件夹中存在`*.lua`,则添加它,例如`plugins/LuaPlugin`
|
||||||
|
3. 然后从该文件夹中进行`lua.loadfile({filename})`(这是lua中的标准方法)
|
||||||
|
4. 最后调用事件和函数`onInit()`
|
||||||
|
5. 如果在执行`onInit()`期间没有发生错误,则可以通过`lua_plugins`命令看到这样的消息:`Lua plugins: LuaPlugin:ok`
|
97
docs/cn/plugins/readme.md
Normal file
97
docs/cn/plugins/readme.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# 插件系统
|
||||||
|
|
||||||
|
### 事件:[这里](./events_list.md)
|
||||||
|
### 类:[这里](./classes.md)
|
||||||
|
|
||||||
|
## 使用带有“Dummy”的库
|
||||||
|
###### (这意味着它没有服务器无法工作,但IDE将指导API)
|
||||||
|
###### (库还在开发中)
|
||||||
|
|
||||||
|
* 使用pip:\
|
||||||
|
`$ pip install KuiToi`
|
||||||
|
* 从源代码安装:\
|
||||||
|
`git clone https://github.com/KuiToi/KuiToi-PyLib`
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
import KuiToi
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
kt = KuiToi("ExamplePlugin")
|
||||||
|
log = kt.log
|
||||||
|
|
||||||
|
def my_event_handler(event_data):
|
||||||
|
log.info(f"{event_data}")
|
||||||
|
|
||||||
|
def load():
|
||||||
|
# 初始化插件
|
||||||
|
ev.register_event("my_event", my_event_handler)
|
||||||
|
log.info("插件已成功加载。")
|
||||||
|
|
||||||
|
|
||||||
|
def start():
|
||||||
|
# 启动插件进程
|
||||||
|
ev.call_event("my_event")
|
||||||
|
ev.call_event("my_event", "一些数据", data="一些数据也是")
|
||||||
|
log.info("插件已成功启动。")
|
||||||
|
|
||||||
|
|
||||||
|
def unload():
|
||||||
|
# 结束所有进程的代码
|
||||||
|
log.info("插件已成功卸载。")
|
||||||
|
```
|
||||||
|
|
||||||
|
您还可以在[example.py](examples/example.py)中找到更广泛的示例。
|
||||||
|
|
||||||
|
* 建议在`load()`后使用`open()`,否则应使用`kt.load()`-在`plugin/<plugin_name>/<filename>`文件夹中创建一个文件
|
||||||
|
* 创建自己的事件:`kt.register_event("my_event", my_event_function)`-
|
||||||
|
* 调用事件:`kt.call_event("my_event")`
|
||||||
|
* 使用数据调用事件:`kt.call_event("my_event", data, data2=data2)`
|
||||||
|
* 基本事件:_稍后会写_
|
||||||
|
|
||||||
|
## 异步函数
|
||||||
|
|
||||||
|
支持async
|
||||||
|
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
import KuiToi
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
kt = KuiToi("Example")
|
||||||
|
log = kt.log
|
||||||
|
|
||||||
|
|
||||||
|
async def my_event_handler(event_data):
|
||||||
|
log.info(f"{event_data}")
|
||||||
|
|
||||||
|
|
||||||
|
async def load():
|
||||||
|
# 初始化插件
|
||||||
|
ev.register_event("my_event", my_event_handler)
|
||||||
|
log.info("插件已成功加载。")
|
||||||
|
|
||||||
|
|
||||||
|
async def start():
|
||||||
|
# 启动插件进程
|
||||||
|
await ev.call_async_event("my_event")
|
||||||
|
await ev.call_async_event("my_event", "一些数据", data="一些数据也是")
|
||||||
|
log.info("插件已成功启动。")
|
||||||
|
|
||||||
|
|
||||||
|
async def unload():
|
||||||
|
# 结束所有进程的代码
|
||||||
|
log.info("插件已成功卸载。")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
您还可以在[async_example.py](examples/async_example.py)中找到更广泛的示例。
|
||||||
|
|
||||||
|
* 创建自己的事件:`kt.register_event("my_event", my_event_function)`(在register_event中检查函数)
|
||||||
|
* 调用async事件:`kt.call_async_event("my_event")`
|
||||||
|
* 使用数据调用async事件:`kt.call_async_event("my_event", data, data2=data2)`
|
||||||
|
* 基本的async事件:_稍后会写_
|
10
docs/cn/readme.md
Normal file
10
docs/cn/readme.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# KuiToi服务器文档
|
||||||
|
|
||||||
|
### 文档尚未完善,但终有一天会完善
|
||||||
|
|
||||||
|
1. 服务器设置和启动 - [这里](./setup)
|
||||||
|
2. 插件和事件系统 - [这里](./plugins)
|
||||||
|
3. Lua的细微差别 - [这里](./plugins/lua)
|
||||||
|
4. 多语言支持 - [这里](./multilanguage)
|
||||||
|
5. KuiToi WebAPI - [这里](./web)
|
||||||
|
6. 将会有新的内容...
|
88
docs/cn/setup/readme.md
Normal file
88
docs/cn/setup/readme.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 来自 KuiToi 服务器的问候
|
||||||
|
|
||||||
|
## 好的,让我们开始吧
|
||||||
|
|
||||||
|
###### _(这里是 Linux 的命令)_
|
||||||
|
|
||||||
|
* 运行它需要 **Python 3.10.x**!只有这个版本才能运行,Python 3.11 不支持...
|
||||||
|
* 您可以像这样检查 Python 版本(你要在这里笑):
|
||||||
|
```bash
|
||||||
|
python3 --version # Python 3.10.6
|
||||||
|
```
|
||||||
|
* 克隆存储库并导航到它
|
||||||
|
* 安装所有必需的内容
|
||||||
|
* 然后,使用我的“脚本”,删除所有不必要的文件并移动到核心源
|
||||||
|
```bash
|
||||||
|
git clone -b Stable https://github.com/kuitoi/KuiToi-Server.git && cd KuiToi-Server
|
||||||
|
pip install -r requirements.txt
|
||||||
|
mv ./src/ $HOME/ktsrc/ && rm -rf ./* && mv $HOME/ktsrc/* . && rm -rf $HOME/ktsrc
|
||||||
|
```
|
||||||
|
* 这是如何检查服务器信息并启动它的方法:
|
||||||
|
```bash
|
||||||
|
python3 main.py --help # 显示所有可用命令
|
||||||
|
python3 main.py # 启动服务器
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
|
||||||
|
* 启动后,将创建 `kuitoi.yaml`
|
||||||
|
* 默认情况下,它如下所示:
|
||||||
|
```yaml
|
||||||
|
!!python/object:modules.ConfigProvider.config_provider.Config
|
||||||
|
Auth:
|
||||||
|
key: null
|
||||||
|
private: true
|
||||||
|
Game:
|
||||||
|
map: gridmap_v2
|
||||||
|
max_cars: 1
|
||||||
|
players: 8
|
||||||
|
Options:
|
||||||
|
debug: false
|
||||||
|
encoding: utf-8
|
||||||
|
language: en
|
||||||
|
log_chat: true
|
||||||
|
speed_limit: 0
|
||||||
|
use_lua: true
|
||||||
|
use_queue: false
|
||||||
|
Server:
|
||||||
|
description: Welcome to KuiToi Server!
|
||||||
|
name: KuiToi-Server
|
||||||
|
server_ip: 0.0.0.0
|
||||||
|
server_port: 30814
|
||||||
|
WebAPI:
|
||||||
|
enabled: false
|
||||||
|
secret_key: 3838ccb03c86cdb386b67fbfdcba62d0
|
||||||
|
server_ip: 127.0.0.1
|
||||||
|
server_port: 8433
|
||||||
|
```
|
||||||
|
### Auth
|
||||||
|
|
||||||
|
* 如果您将 `private: false` 并且不设置 `key`,服务器将请求一个 BeamMP 密钥,没有它无法启动。
|
||||||
|
* 输入 BeamMP 密钥后,服务器将出现在启动器列表中。
|
||||||
|
* 您可以在此处获取密钥:[https://beammp.com/k/keys ↗](https://beammp.com/k/keys)
|
||||||
|
|
||||||
|
### Game
|
||||||
|
|
||||||
|
* `map` 仅为地图名称,即打开具有地图的 mod 在 `map.zip/levels` - 地图名称将在那里,那就是我们插入的地方。
|
||||||
|
* `max_cars` - 每个玩家的最大汽车数量
|
||||||
|
* `players` - 最大玩家数
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* `debug` - 是否输出调试消息(仅适用于有经验的用户,会略微降低性能)
|
||||||
|
* `encoding` - 使用哪种编码打开文件
|
||||||
|
* `language` - 服务器将使用哪种语言启动(当前可用:en,ru)
|
||||||
|
* `log_chat` - 是否将聊天输出到控制台
|
||||||
|
* `speed_limit` - 下载 mod 的下载速度限制(以 MB/s 为单位)
|
||||||
|
* `use_lua` - 启用 lua 支持
|
||||||
|
* `use_queue` - 按队列下载 mod,即一次只能下载一个客户端
|
||||||
|
|
||||||
|
### Server
|
||||||
|
|
||||||
|
* `description` - BeamMP 启动器的服务器描述
|
||||||
|
* `name` - BeamMP 启动器的服务器名称
|
||||||
|
* `server_ip` - 分配给服务器的 IP 地址(仅适用于有经验的用户,默认为 0.0.0.0)
|
||||||
|
* `server_port` - 服务器将在哪个端口上工作
|
||||||
|
|
||||||
|
### WebAPI
|
||||||
|
##### _文档尚未准备好_
|
13
docs/cn/web/readme.md
Normal file
13
docs/cn/web/readme.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# 服务器的 WebAPI
|
||||||
|
|
||||||
|
## 可用的端点
|
||||||
|
|
||||||
|
* `/stop`:
|
||||||
|
* 必需参数:
|
||||||
|
* `secret_key` - 在服务器配置中指定的密钥
|
||||||
|
|
||||||
|
|
||||||
|
* `/event.get`
|
||||||
|
* 这个端点还没有准备好
|
||||||
|
* 必需参数:
|
||||||
|
* `secret_key` - 在服务器配置中指定的密钥
|
298
docs/en/plugins/classes.md
Normal file
298
docs/en/plugins/classes.md
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
Sure, here's a translation of the text:
|
||||||
|
|
||||||
|
# Passed Classes
|
||||||
|
|
||||||
|
## Worth looking at
|
||||||
|
|
||||||
|
1. What are `*args` and `**kwargs`? -> [Post on Habr (RU)](https://habr.com/ru/companies/ruvds/articles/482464/)
|
||||||
|
|
||||||
|
## KuiToi
|
||||||
|
_`kt = KuiToi("PluginName"")`_
|
||||||
|
|
||||||
|
### kt.log
|
||||||
|
_Constant_\
|
||||||
|
Returns a pre-configured logger
|
||||||
|
|
||||||
|
### kt.name
|
||||||
|
_Constant_\
|
||||||
|
Returns the name of the plugin
|
||||||
|
|
||||||
|
### kt.dir
|
||||||
|
_Constant_\
|
||||||
|
Returns the directory of the plugin
|
||||||
|
|
||||||
|
### kt.open()
|
||||||
|
_Parameters are the same as for open()_\
|
||||||
|
Opens a file in kt.dir
|
||||||
|
|
||||||
|
### kt.register_event(event_name: str, event_func: function)
|
||||||
|
_`event_name: str` -> The name of the event that `event_func` will be called on._\
|
||||||
|
_`event_func: function` -> The function that will be called._
|
||||||
|
|
||||||
|
In `event_func`, you can pass both regular functions and async functions - you don't need to make them async beforehand.\
|
||||||
|
You can also create your own events with your own names.\
|
||||||
|
You can register an unlimited number of events.
|
||||||
|
|
||||||
|
### kt.call_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> The name of the event to call._\
|
||||||
|
_`*args, **kwargs` -> Arguments to be passed to the function._
|
||||||
|
|
||||||
|
### **async** kt.call_async_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> The name of the event to call._\
|
||||||
|
_`*args, **kwargs` -> Arguments to be passed to the function._\
|
||||||
|
_Must be called with `await`_
|
||||||
|
|
||||||
|
###### _I recommend familiarizing yourself with *args, **kwargs_, there is a link at the beginning
|
||||||
|
Data is passed to all events in the form of: `{"event_name": event_name, "args": args, "kwargs": kwargs}`\
|
||||||
|
`args: list` -> Represents an array of data passed to the event\
|
||||||
|
`kwargs: dict` -> Represents a dictionary of data passed to the event
|
||||||
|
The data will be returned from all successful attempts in an array.
|
||||||
|
|
||||||
|
### kt.call_lua_event(event_name: str, *args) -> list:
|
||||||
|
_`event_name: str` -> The name of the event to call._\
|
||||||
|
_`*args` -> Arguments to be passed to the function._
|
||||||
|
|
||||||
|
Added to support backward compatibility.\
|
||||||
|
The lua function is called with a direct transmission of arguments `lua_func(*args)`
|
||||||
|
|
||||||
|
### kt.get_player([pid: int], [nick: str]) -> Player | None:
|
||||||
|
_`pid: int` -> Player ID - The identifier of the player._\
|
||||||
|
_`nick: str` -> Player Nickname - The name of the player._
|
||||||
|
|
||||||
|
The method returns a player object by their `pid` or `nick`.\
|
||||||
|
If the player cannot be found, `None` will be returned.
|
||||||
|
|
||||||
|
### kt.get_players() -> List[Player] | list:
|
||||||
|
|
||||||
|
The method returns an array with all players.\
|
||||||
|
The array will be empty if there are no players.
|
||||||
|
|
||||||
|
### kt.players_counter() -> int:
|
||||||
|
|
||||||
|
The method returns the number of players currently online.
|
||||||
|
|
||||||
|
### kt.is_player_connected([pid: int], [nick: str]) -> bool:
|
||||||
|
_`pid: int` -> Player ID - The identifier of the player._\
|
||||||
|
_`nick: str` -> Player Nickname - The name of the player._
|
||||||
|
|
||||||
|
The method returns a player object by their `pid` or `nick`.
|
||||||
|
|
||||||
|
## Player (or Client)
|
||||||
|
_`pl = kt.get_player()`_\
|
||||||
|
_`pl = event_data['kwargs']['player']`_
|
||||||
|
|
||||||
|
### pl.log -> Logger
|
||||||
|
_Constant_\
|
||||||
|
Returns a pre-configured logger
|
||||||
|
|
||||||
|
### pl.addr -> str
|
||||||
|
_Constant_\
|
||||||
|
Returns the IP address of the player
|
||||||
|
|
||||||
|
### pl.pid -> int
|
||||||
|
### pl.cid -> int
|
||||||
|
_Constant_\
|
||||||
|
Returns the client ID _(pid: PlayerId = cid: ClientId)_
|
||||||
|
|
||||||
|
### pl.key -> str
|
||||||
|
_Constant_\
|
||||||
|
Returns the key passed during authentication
|
||||||
|
|
||||||
|
### pl.nick -> str
|
||||||
|
_Variable_\
|
||||||
|
The nickname passed during authentication from the BeamMP server, can be changed, consequences are untested
|
||||||
|
|
||||||
|
### pl.roles -> str
|
||||||
|
_Variable_\
|
||||||
|
The role passed during authentication from the BeamMP server, can be changed (if an incorrect role is set, unexpected things may happen.)
|
||||||
|
|
||||||
|
### pl.guest -> bool
|
||||||
|
_Constant_\
|
||||||
|
Returns whether the player is a guest, passed during authentication from the BeamMP server
|
||||||
|
|
||||||
|
### pl.identifiers -> dict
|
||||||
|
_Constant_\
|
||||||
|
Identifiers passed during authentication from the BeamMP server.
|
||||||
|
|
||||||
|
### pl.ready -> bool
|
||||||
|
_Constant, changed by the core_\
|
||||||
|
Returns a bool value, if True -> the player has downloaded all resources, loaded on the map
|
||||||
|
|
||||||
|
### pl.cars -> dict
|
||||||
|
_Constant, changed by the core_\
|
||||||
|
Returns a dictionary of cars like thisSure, here's the translation:
|
||||||
|
|
||||||
|
# Passed Classes
|
||||||
|
|
||||||
|
## Worth looking at
|
||||||
|
|
||||||
|
1. What are `*args` and `**kwargs`? -> [Post on Habr ↗](https://habr.com/ru/companies/ruvds/articles/482464/)
|
||||||
|
|
||||||
|
## KuiToi
|
||||||
|
_`kt = KuiToi("PluginName"")`_
|
||||||
|
|
||||||
|
### kt.log
|
||||||
|
_Constant_\
|
||||||
|
Returns a pre-configured logger
|
||||||
|
|
||||||
|
### kt.name
|
||||||
|
_Constant_\
|
||||||
|
Returns the name of the plugin
|
||||||
|
|
||||||
|
### kt.dir
|
||||||
|
_Constant_\
|
||||||
|
Returns the directory of the plugin
|
||||||
|
|
||||||
|
### kt.open()
|
||||||
|
_Parameters are the same as for open()_\
|
||||||
|
Opens a file in kt.dir
|
||||||
|
|
||||||
|
### kt.register_event(event_name: str, event_func: function)
|
||||||
|
_`event_name: str` -> The name of the event that `event_func` will be called on._\
|
||||||
|
_`event_func: function` -> The function that will be called._
|
||||||
|
|
||||||
|
In `event_func`, you can pass both regular functions and async functions - you don't need to make them async beforehand.\
|
||||||
|
You can also create your own events with your own names.\
|
||||||
|
You can register an unlimited number of events.
|
||||||
|
|
||||||
|
### kt.call_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> The name of the event to call._\
|
||||||
|
_`*args, **kwargs` -> Arguments to be passed to the function._
|
||||||
|
|
||||||
|
### **async** kt.call_async_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> The name of the event to call._\
|
||||||
|
_`*args, **kwargs` -> Arguments to be passed to the function._\
|
||||||
|
_Must be called with `await`_
|
||||||
|
|
||||||
|
###### _I recommend familiarizing yourself with *args, **kwargs_, there is a link at the beginning
|
||||||
|
Data is passed to all events in the form of: `{"event_name": event_name, "args": args, "kwargs": kwargs}`\
|
||||||
|
`args: list` -> Represents an array of data passed to the event\
|
||||||
|
`kwargs: dict` -> Represents a dictionary of data passed to the event
|
||||||
|
The data will be returned from all successful attempts in an array.
|
||||||
|
|
||||||
|
### kt.call_lua_event(event_name: str, *args) -> list:
|
||||||
|
_`event_name: str` -> The name of the event to call._\
|
||||||
|
_`*args` -> Arguments to be passed to the function._
|
||||||
|
|
||||||
|
Added to support backward compatibility.\
|
||||||
|
The lua function is called with a direct transmission of arguments `lua_func(*args)`
|
||||||
|
|
||||||
|
### kt.get_player([pid: int], [nick: str]) -> Player | None:
|
||||||
|
_`pid: int` -> Player ID - The identifier of the player._\
|
||||||
|
_`nick: str` -> Player Nickname - The name of the player._
|
||||||
|
|
||||||
|
The method returns a player object by their `pid` or `nick`.\
|
||||||
|
If the player cannot be found, `None` will be returned.
|
||||||
|
|
||||||
|
### kt.get_players() -> List[Player] | list:
|
||||||
|
|
||||||
|
The method returns an array with all players.\
|
||||||
|
The array will be empty if there are no players.
|
||||||
|
|
||||||
|
### kt.players_counter() -> int:
|
||||||
|
|
||||||
|
The method returns the number of players currently online.
|
||||||
|
|
||||||
|
### kt.is_player_connected([pid: int], [nick: str]) -> bool:
|
||||||
|
_`pid: int` -> Player ID - The identifier of the player._\
|
||||||
|
_`nick: str` -> Player Nickname - The name of the player._
|
||||||
|
|
||||||
|
The method returns a player object by their `pid` or `nick`.
|
||||||
|
|
||||||
|
## Player (or Client)
|
||||||
|
_`pl = kt.get_player()`_\
|
||||||
|
_`pl = event_data['kwargs']['player']`_
|
||||||
|
|
||||||
|
### pl.log -> Logger
|
||||||
|
_Constant_\
|
||||||
|
Returns a preconfigured logger.
|
||||||
|
|
||||||
|
### pl.addr -> str
|
||||||
|
_Constant_\
|
||||||
|
Returns the player's IP address.
|
||||||
|
|
||||||
|
### pl.pid -> int
|
||||||
|
### pl.cid -> int
|
||||||
|
_Constant_\
|
||||||
|
Returns the client ID _(pid: PlayerId = cid: ClientId)_.
|
||||||
|
|
||||||
|
### pl.key -> str
|
||||||
|
_Constant_\
|
||||||
|
Returns the key passed during authorization.
|
||||||
|
|
||||||
|
### pl.nick -> str
|
||||||
|
_Variable_\
|
||||||
|
Nickname passed during authorization from the BeamMP server, can be changed, consequences are not tested.
|
||||||
|
|
||||||
|
### pl.roles -> str
|
||||||
|
_Variable_\
|
||||||
|
Role passed during authorization from the BeamMP server, can be changed (If the wrong role is set, unexpected behavior may occur.)
|
||||||
|
|
||||||
|
### pl.guest -> bool
|
||||||
|
_Constant_\
|
||||||
|
Returns whether the player is a guest, passed during authorization from the BeamMP server.
|
||||||
|
|
||||||
|
### pl.identifiers -> dict
|
||||||
|
_Constant_\
|
||||||
|
Identifiers passed during authorization from the BeamMP server.
|
||||||
|
|
||||||
|
### pl.ready -> bool
|
||||||
|
_Constant, changed by the core_\
|
||||||
|
Returns a bool value, if True -> player has downloaded all resources and loaded on the map.
|
||||||
|
|
||||||
|
### pl.cars -> dict
|
||||||
|
_Constant, changed by the core_\
|
||||||
|
Returns a dictionary of cars by type:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
1: {
|
||||||
|
"packet": car_packet,
|
||||||
|
"json": car_json,
|
||||||
|
"json_ok": bool(car_json),
|
||||||
|
"snowman": snowman,
|
||||||
|
"over_spawn": (snowman and allow_snowman) or over_spawn,
|
||||||
|
"pos": {
|
||||||
|
"pos":[0,0,0],
|
||||||
|
"rvel":[0,0,0],
|
||||||
|
"rot":[0,0,0],
|
||||||
|
"vel":[0,0,0],
|
||||||
|
"tim":0,
|
||||||
|
"ping":0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2: ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Where `1` - car_id\
|
||||||
|
Where `pkt` - Unprocessed packet that came from the client (For very experienced users)\
|
||||||
|
Where `json` - Processed packet stored as dict\
|
||||||
|
Where `json_ok` - Whether the core was able to process the packet\
|
||||||
|
Where `snowman` - Is the car a snowman\
|
||||||
|
Where `over_spawn` - Is the car spawned over the limit (Allowed through plugins)\
|
||||||
|
Where `pos` - Car position (Passed through UDP)
|
||||||
|
|
||||||
|
### pl.last_position -> dict
|
||||||
|
_Constant, changed by the core_\
|
||||||
|
Returns the player's last position
|
||||||
|
|
||||||
|
### **async** pl.kick([reason: str = "Kicked!"]) -> None
|
||||||
|
_`reason: str` -> Kick reason. Parameter is optional, by default: `Kicked!`_\
|
||||||
|
Kicks the player from the server.
|
||||||
|
|
||||||
|
### **async** pl.send_message(message: str, [to_all: bool = True]) -> None
|
||||||
|
_`message: str` -> Message text, sent without "Server:"_\
|
||||||
|
_`to_all: bool` -> Should this message be sent to everyone? Parameter is optional, by default: `True`_\
|
||||||
|
Sends a message to the player or everyone.
|
||||||
|
|
||||||
|
### **async** pl.send_event(event_name: str, event_data: Any, [to_all: bool = True]) -> None
|
||||||
|
_`event_name: str` -> Name of the event that will be called_\
|
||||||
|
_`event_data: Any` -> Data sent to the event._\
|
||||||
|
_`to_all: bool` -> Should this message be sent to everyone? Parameter is optional, by default: `True`_\
|
||||||
|
Sends an event to the client.\
|
||||||
|
If event_data is a tuple, list, dict, then before sending the core converts it to JSON via json.dumps(event_data)\
|
||||||
|
Otherwise, the data will be a string without regulation.
|
||||||
|
|
||||||
|
### **async** pl.delete_car(self, car_id: int) -> None
|
||||||
|
_`car_id: int` -> Car ID_\
|
||||||
|
Deletes the player's car.
|
4
docs/en/plugins/events_list.md
Normal file
4
docs/en/plugins/events_list.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# List of available events
|
||||||
|
|
||||||
|
Most events will receive `pl = data['kwargs']['player']`, you can find a description [here](./classes.md)
|
||||||
|
|
27
docs/en/plugins/lua/example.lua
Normal file
27
docs/en/plugins/lua/example.lua
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
print("example.lua")
|
||||||
|
|
||||||
|
--CreateTimer Testing
|
||||||
|
local mytimer = MP.CreateTimer()
|
||||||
|
--.--.--.--.--.--.--.
|
||||||
|
|
||||||
|
--GetOSName Testing
|
||||||
|
print("OS Name: "..MP.GetOSName())
|
||||||
|
--.--.--.--.--.--.-
|
||||||
|
|
||||||
|
--GetServerVersion Testing
|
||||||
|
local major, minor, patch = MP.GetServerVersion()
|
||||||
|
print("Server Version: "..major.."."..minor.."."..patch)
|
||||||
|
--.--.--.--.--.--.--.--.--
|
||||||
|
|
||||||
|
--Events Testing--
|
||||||
|
function handleChat(player_id, player_name, message)
|
||||||
|
print("Lua handleChat:", player_id, player_name, message, "; Uptime: "..mytimer:GetCurrent())
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
MP.RegisterEvent("onChatMessage", "handleChat")
|
||||||
|
--.--.--.--.--.--.
|
||||||
|
|
||||||
|
function onInit()
|
||||||
|
print("Initializing ready!")
|
||||||
|
end
|
35
docs/en/plugins/lua/readme.md
Normal file
35
docs/en/plugins/lua/readme.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Providing Backward Compatibility for BeamMP Lua
|
||||||
|
|
||||||
|
KiuToi provides almost full support for lua plugins with BeamMP. All necessary methods have been created, and testing has revealed the following nuances:
|
||||||
|
|
||||||
|
KiuToi does not support: `MP.Set()`
|
||||||
|
|
||||||
|
#### Economic Rework V2.0 (Paid, Discord (RU): [Hlebushek](https://discordapp.com/users/449634697593749516))
|
||||||
|
|
||||||
|
1. To obtain `pluginPath`, use: `debug.getinfo(1).source:gsub("\\","/")` => `debug.getinfo(1).source:gsub("\\","/"):gsub("@", "")` as the path returns with `@`, which broke the plugin.
|
||||||
|
|
||||||
|
#### Cobalt Essentials V1.7.5 (Free, [github](https://github.com/prestonelam2003/CobaltEssentials/))
|
||||||
|
|
||||||
|
1. To obtain `pluginPath`, use: `debug.getinfo(1).source:gsub("\\","/")` => `debug.getinfo(1).source:gsub("\\","/"):gsub("@", "")` as the path returns with `@`, which broke the plugin.
|
||||||
|
2. All `require()` statements had to be moved after `onInit`.
|
||||||
|
3. In some cases, `MP.RegisterEvent` had to be moved after the function declaration, i.e.:
|
||||||
|
```lua
|
||||||
|
--This is incorrect, registration may fail
|
||||||
|
MP.RegisterEvent("onPlayerAuth","onPlayerAuth")
|
||||||
|
function onPlayerAuth(name, role, isGuest)
|
||||||
|
-- Some plugin code
|
||||||
|
end
|
||||||
|
|
||||||
|
--This is the correct version
|
||||||
|
MP.RegisterEvent("onPlayerAuth","onPlayerAuth")
|
||||||
|
```
|
||||||
|
|
||||||
|
### A Little About How it Works
|
||||||
|
|
||||||
|
Plugin loading goes through several stages:
|
||||||
|
|
||||||
|
1. The `plugins/` folder is scanned.
|
||||||
|
2. If the folder is not in PyPlugins and there are `*.lua` files in the folder, then it is added as a plugin folder, let's say it will be `plugins/LuaPlugin`
|
||||||
|
3. Next, `lua.loadfile({filename})` is performed from this folder (this is the standard method in lua).
|
||||||
|
4. Finally, the `onInit()` function is called, and an event is triggered.
|
||||||
|
5. If no errors occur during the execution of `onInit()`, you can see the message `Lua plugins: LuaPlugin:ok` through the `lua_plugins` command.
|
@ -1,12 +1,18 @@
|
|||||||
# Plugin System
|
Это описание системы плагинов для KuiToi сервера на Python:
|
||||||
|
|
||||||
## Installing the Library with "Stubs"
|
## Events
|
||||||
###### (This means that it will not work without a server, but the IDE will suggest the API)
|
### Events list: [here](./events_list.md)
|
||||||
###### (The library is still under development)
|
|
||||||
|
## Classes
|
||||||
|
### Classes list: [here](./classes.md)
|
||||||
|
|
||||||
|
## Installing the library with "stubs"
|
||||||
|
###### (This means it won't work without the server, but your IDE will suggest the API)
|
||||||
|
###### (The library is still in development)
|
||||||
|
|
||||||
* Using pip:\
|
* Using pip:\
|
||||||
`$ pip install KuiToi`
|
`$ pip install KuiToi`
|
||||||
* From source code:\
|
* From source:\
|
||||||
`git clone https://github.com/KuiToi/KuiToi-PyLib`
|
`git clone https://github.com/KuiToi/KuiToi-PyLib`
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
@ -17,7 +23,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
kt = KuiToi("Example")
|
kt = KuiToi("ExamplePlugin")
|
||||||
log = kt.log
|
log = kt.log
|
||||||
|
|
||||||
def my_event_handler(event_data):
|
def my_event_handler(event_data):
|
||||||
@ -25,14 +31,14 @@ def my_event_handler(event_data):
|
|||||||
|
|
||||||
def load():
|
def load():
|
||||||
# Plugin initialization
|
# Plugin initialization
|
||||||
ev.register_event("my_event", my_event_handler)
|
kt.register_event("my_event", my_event_handler)
|
||||||
log.info("Plugin loaded successfully.")
|
log.info("Plugin loaded successfully.")
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
# Running plugin processes
|
# Starting plugin processes
|
||||||
ev.call_event("my_event")
|
kt.call_event("my_event")
|
||||||
ev.call_event("my_event", "Some data", data="some data too")
|
kt.call_event("my_event", "Some data", data="some data too")
|
||||||
log.info("Plugin started successfully.")
|
log.info("Plugin started successfully.")
|
||||||
|
|
||||||
|
|
||||||
@ -41,15 +47,17 @@ def unload():
|
|||||||
log.info("Plugin unloaded successfully.")
|
log.info("Plugin unloaded successfully.")
|
||||||
```
|
```
|
||||||
|
|
||||||
* It is recommended to use `open()` after `load()`. Otherwise, use `kt.load()` - creates a file in the `plugin/<plugin_name>/<filename>` folder.
|
A more comprehensive example can also be found in [example.py](examples/example.py)
|
||||||
* Creating your own event: `kt.register_event("my_event", my_event_function)`
|
|
||||||
|
* It is recommended to use `open()` after `load()`, otherwise use `kt.load()` - It creates a file in the `plugin/<plugin_name>/<filename>` folder.
|
||||||
|
* Creating your own event: `kt.register_event("my_event", my_event_function)` -
|
||||||
* Calling an event: `kt.call_event("my_event")`
|
* Calling an event: `kt.call_event("my_event")`
|
||||||
* Calling an event with data: `kt.call_event("my_event", data, data2=data2)`
|
* Calling an event with data: `kt.call_event("my_event", data, data2=data2)`
|
||||||
* Basic events: _Will write later_
|
* Base events: _To be added later_
|
||||||
|
|
||||||
## Async Functions
|
## Async functions
|
||||||
|
|
||||||
Async support is available.
|
Async support is available
|
||||||
|
|
||||||
```python
|
```python
|
||||||
try:
|
try:
|
||||||
@ -64,17 +72,17 @@ log = kt.log
|
|||||||
async def my_event_handler(event_data):
|
async def my_event_handler(event_data):
|
||||||
log.info(f"{event_data}")
|
log.info(f"{event_data}")
|
||||||
|
|
||||||
|
|
||||||
async def load():
|
async def load():
|
||||||
# Plugin initialization
|
# Plugin initialization
|
||||||
ev.register_event("my_event", my_event_handler)
|
kt.register_event("my_event", my_event_handler)
|
||||||
log.info("Plugin loaded successfully.")
|
log.info("Plugin loaded successfully.")
|
||||||
|
|
||||||
|
|
||||||
async def start():
|
async def start():
|
||||||
# Running plugin processes
|
# Starting plugin processes
|
||||||
await ev.call_async_event("my_event")
|
await kt.call_async_event("my_event")
|
||||||
await ev.call_async_event("my_event", "Some data", data="some data too")
|
await kt.call_async_event("my_event", "Some data", data="some data too")
|
||||||
log.info("Plugin started successfully.")
|
log.info("Plugin started successfully.")
|
||||||
|
|
||||||
|
|
||||||
@ -84,9 +92,9 @@ async def unload():
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
A more extensive example can also be found in [async_example.py](./async_example.py).
|
A more comprehensive example can also be found in [async_example.py](examples/async_example.py)
|
||||||
|
|
||||||
* Creating your own event: `kt.register_event("my_event", my_event_function)` (register_event checks for function)
|
* Creating your own event: `kt.register_event("my_event", my_event_function)` (register_event has a function check)
|
||||||
* Calling an async event: `kt.call_async_event("my_event")`
|
* Calling an async event: `kt.call_async_event("my_event")`
|
||||||
* Calling an async event with data: `kt.call_async_event("my_event", data, data2=data2)`
|
* Calling an async event with data: `kt.call_async_event("my_event", data, data2=data2)`
|
||||||
* Basic async events: _Will write later_
|
* Base async events: _To be added later_
|
@ -1,9 +1,10 @@
|
|||||||
# Documentation for KuiToi Server
|
# Documentation for KuiToi Server
|
||||||
|
|
||||||
### The documentation is not perfect yet, but it will be one day
|
### The documentation is not yet perfect, but someday it will be.
|
||||||
|
|
||||||
1. Setup and Start server - [here](setup)
|
1. Setup and Launching the Server - [here](./setup)
|
||||||
2. Plugins and Events system - [here](plugins)
|
2. Plugins and Event System - [here](./plugins)
|
||||||
3. MultiLanguage - [here](./multilanguage)
|
3. Nuances of Working with Lua - [here](./plugins/lua)
|
||||||
4. KuiToi WebAPI - [here](./web)
|
4. Multilanguage Support - [here](./multilanguage)
|
||||||
5. Something new...
|
5. KuiToi WebAPI - [here](./web)
|
||||||
|
6. Something new will be added here soon...
|
@ -1,31 +1,31 @@
|
|||||||
# Greetings from KuiToi Server
|
# Greetings from KuiToi Server
|
||||||
|
|
||||||
## Well, let's begin
|
## Well, let's start
|
||||||
|
|
||||||
###### _(Here are the commands for Linux)_
|
###### _(Here are the commands for Linux)_
|
||||||
|
|
||||||
* **Python 3.10.x** is required to run the server! It won't work on Python 3.11...
|
* **Python 3.10.x** is required to run it! Only this version works, it won't work on Python 3.11...
|
||||||
* You can check the version of your Python installation with the following command:
|
* You can check your Python version like this (you have to laugh here):
|
||||||
```bash
|
```bash
|
||||||
python3 --version # Python 3.10.6
|
python3 --version # Python 3.10.6
|
||||||
```
|
```
|
||||||
* Clone the repository and navigate to it.
|
* Clone the repository and navigate to it
|
||||||
* Install everything that's needed.
|
* Install everything necessary
|
||||||
* Then, using my "script", remove all unnecessary files and move to the core source code.
|
* Then, using my "script", remove all unnecessary files and move to the core source
|
||||||
```bash
|
```bash
|
||||||
git clone -b Stable https://github.com/kuitoi/KuiToi-Server.git && cd KuiToi-Server
|
git clone -b Stable https://github.com/kuitoi/KuiToi-Server.git && cd KuiToi-Server
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
mv ./src/ $HOME/ktsrc/ && rm -rf ./* && mv $HOME/ktsrc/* . && rm -rf $HOME/ktsrc
|
mv ./src/ $HOME/ktsrc/ && rm -rf ./* && mv $HOME/ktsrc/* . && rm -rf $HOME/ktsrc
|
||||||
```
|
```
|
||||||
* Here's how to view information about the server and start it:
|
* Here's how you can check server info and start it:
|
||||||
```bash
|
```bash
|
||||||
python3 main.py --help # Displays all available commands
|
python3 main.py --help # Shows all available commands
|
||||||
python3 main.py # Starts the server
|
python3 main.py # Starts the server
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
* After starting the server, a `kuitoi.yaml` file will be created.
|
* After starting, `kuitoi.yaml` will be created
|
||||||
* By default, it looks like this:
|
* By default, it looks like this:
|
||||||
```yaml
|
```yaml
|
||||||
!!python/object:modules.ConfigProvider.config_provider.Config
|
!!python/object:modules.ConfigProvider.config_provider.Config
|
||||||
@ -36,40 +36,53 @@ Game:
|
|||||||
map: gridmap_v2
|
map: gridmap_v2
|
||||||
max_cars: 1
|
max_cars: 1
|
||||||
players: 8
|
players: 8
|
||||||
Server:
|
Options:
|
||||||
debug: false
|
debug: false
|
||||||
description: Welcome to KuiToi Server!
|
encoding: utf-8
|
||||||
language: en
|
language: en
|
||||||
|
log_chat: true
|
||||||
|
speed_limit: 0
|
||||||
|
use_lua: true
|
||||||
|
use_queue: false
|
||||||
|
Server:
|
||||||
|
description: Welcome to KuiToi Server!
|
||||||
name: KuiToi-Server
|
name: KuiToi-Server
|
||||||
server_ip: 0.0.0.0
|
server_ip: 0.0.0.0
|
||||||
server_port: 30813
|
server_port: 30814
|
||||||
WebAPI:
|
WebAPI:
|
||||||
enabled: false
|
enabled: false
|
||||||
secret_key: <random_key>
|
secret_key: 3838ccb03c86cdb386b67fbfdcba62d0
|
||||||
server_ip: 127.0.0.1
|
server_ip: 127.0.0.1
|
||||||
server_port: 8433
|
server_port: 8433
|
||||||
|
|
||||||
```
|
```
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
* If you set `private: false` and do not set a `key`, the server will request a BeamMP key and will not start without it.
|
* If you set `private: false` and don't set a `key`, the server will request a BeamMP key and won't start without it.
|
||||||
* By entering a BeamMP key, the server will appear in the launcher list.
|
* After entering a BeamMP key, the server will appear in the launcher list.
|
||||||
* You can get a key here: [https://beammp.com/k/keys ↗](https://beammp.com/k/keys)
|
* You can get the key here: [https://beammp.com/k/keys ↗](https://beammp.com/k/keys)
|
||||||
|
|
||||||
### Game
|
### Game
|
||||||
|
|
||||||
* `map` specifies only the name of the map. That is, open the mod with the map in `map.zip/levels` - the name of the map will be there, and that's what you need to insert.
|
* `map` is only the name of the map, i.e. open the mod with the map in `map.zip/levels` - the name of the map will be there, that's what we insert.
|
||||||
* `max_cars` - the maximum number of cars per player
|
* `max_cars` - Maximum number of cars per player
|
||||||
* `players` - the maximum number of players
|
* `players` - Maximum number of players
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* `debug` - Whether to output debug messages (for experienced users only, slightly reduces performance)
|
||||||
|
* `encoding` - Which encoding to use to open files
|
||||||
|
* `language` - Which language the server will start with (currently available: en, ru)
|
||||||
|
* `log_chat` - Whether to output chat to the console
|
||||||
|
* `speed_limit` - Download speed limit for mods (in MB/s)
|
||||||
|
* `use_lua` - Enable lua support
|
||||||
|
* `use_queue` - Download mods in queue, i.e. only 1 client can download at a time
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
||||||
* `debug` - should debug messages be displayed (for experienced users only; slightly affects performance)
|
* `description` - Server description for the BeamMP launcher
|
||||||
* `description` - server description for the BeamMP launcher
|
* `name` - Server name for the BeamMP launcher
|
||||||
* `language` - the language in which the server will run (currently available: en, ru)
|
* `server_ip` - IP address to assign to the server (for experienced users only, defaults to 0.0.0.0)
|
||||||
* `name` - server name for the BeamMP launcher
|
* `server_port` - On which port the server will work
|
||||||
* `server_ip` - the IP address to be used by the server (for experienced users only; defaults to 0.0.0.0)
|
|
||||||
* `server_port` - the port on which the server will run
|
|
||||||
|
|
||||||
### WebAPI
|
### WebAPI
|
||||||
##### _Docs are not ready yet_
|
##### _Docs are not ready_
|
@ -1,4 +1,5 @@
|
|||||||
# Choose your language
|
# Choose your language
|
||||||
|
|
||||||
### [English](./en)
|
|
||||||
### [Русский](./ru)
|
### [Русский](./ru)
|
||||||
|
### [English](./en) (AI translation)
|
||||||
|
### [Chinese](./cn) (AI translation)
|
||||||
|
175
docs/ru/plugins/classes.md
Normal file
175
docs/ru/plugins/classes.md
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
# Передаваемые классы
|
||||||
|
|
||||||
|
## Стоит ознакомится
|
||||||
|
|
||||||
|
1. Что такое `*args` и `**kwargs`? -> [Пост на habr](https://habr.com/ru/companies/ruvds/articles/482464/)
|
||||||
|
|
||||||
|
## KuiToi
|
||||||
|
_`kt = KuiToi("PluginName"")`_
|
||||||
|
|
||||||
|
### kt.log
|
||||||
|
_Константа_\
|
||||||
|
Вернёт преднастроенный логгер
|
||||||
|
|
||||||
|
### kt.name
|
||||||
|
_Константа_\
|
||||||
|
Вернёт имя плагина
|
||||||
|
|
||||||
|
### kt.dir
|
||||||
|
_Константа_\
|
||||||
|
Вернёт папку плагина
|
||||||
|
|
||||||
|
### kt.open()
|
||||||
|
_Параметры как у open()_\
|
||||||
|
Открывает файл в kt.dir
|
||||||
|
|
||||||
|
### kt.register_event(event_name: str, event_func: function)
|
||||||
|
_`event_name: str` -> Имя ивента, по которому будет вызвана `event_func`._\
|
||||||
|
_`event_func: function` -> Функция, которая будет вызвана._
|
||||||
|
|
||||||
|
В `event_func` можно передавать как обычную функцию, так и async - await не нужно делать заранее.\
|
||||||
|
Ивенты можно создавать так же свои, со своим именем.\
|
||||||
|
Зарегистрировать можно не ограниченное кол-во ивентов.
|
||||||
|
|
||||||
|
### kt.call_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> Имя ивента, который будет вызван._\
|
||||||
|
_`*args, **kwargs` -> Аргументы, передаваемые во функции._
|
||||||
|
|
||||||
|
### **async** kt.call_async_event(event_name: str, *args, **kwargs) -> list:
|
||||||
|
_`event_name: str` -> Имя ивента, который будет вызван._\
|
||||||
|
_`*args, **kwargs` -> Аргументы, передаваемые во функции._\
|
||||||
|
_Необходимо вызывать с `await`_
|
||||||
|
|
||||||
|
###### _Советую ознакомиться с *args, **kwargs_, ссылка есть в начале
|
||||||
|
Данные во все ивенты приходят по типу: `{"event_name": event_name, "args": args, "kwargs": kwargs}`\
|
||||||
|
`args: list` -> Представляет из себя массив данных, которые переданы в ивент\
|
||||||
|
`kwargs: dict` -> Представляет из себя словарь данных, которые переданы в ивент
|
||||||
|
Данные вернутся от всех удачных волнений в массиве.
|
||||||
|
|
||||||
|
### kt.call_lua_event(event_name: str, *args) -> list:
|
||||||
|
_`event_name: str` -> Имя ивента, который будет вызван._\
|
||||||
|
_`*args` -> Аргументы, передаваемые во функции._
|
||||||
|
|
||||||
|
Добавлено для поддержки обратной совместимости.\
|
||||||
|
lua функция вызывается с прямой передачей аргументов `lua_func(*args)`
|
||||||
|
|
||||||
|
### kt.get_player([pid: int], [nick: str]) -> Player | None:
|
||||||
|
_`pid: int` -> Player ID - Идентификатор игрока._\
|
||||||
|
_`nick: str` -> Player Nick - Ник игрока._
|
||||||
|
|
||||||
|
Метод возвращает объект игрока по его `pid` или `nick`.\
|
||||||
|
Если не удалось найти игрока вернётся `None`.
|
||||||
|
|
||||||
|
### kt.get_players() -> List[Player] | list:
|
||||||
|
|
||||||
|
Метод возвращает массив со всеми игроками.\
|
||||||
|
Массив будет пустой, если игроков нет.
|
||||||
|
|
||||||
|
### kt.players_counter() -> int:
|
||||||
|
|
||||||
|
Метод возвращает количество игроков, которые сейчас онлайн.
|
||||||
|
|
||||||
|
### kt.is_player_connected([pid: int], [nick: str]) -> bool:
|
||||||
|
_`pid: int` -> Player ID - Идентификатор игрока._\
|
||||||
|
_`nick: str` -> Player Nick - Ник игрока._
|
||||||
|
|
||||||
|
Метод возвращает объект игрока по его `pid`, `nick`.
|
||||||
|
|
||||||
|
## Player (или Client)
|
||||||
|
_`pl = kt.get_player()`_\
|
||||||
|
_`pl = event_data['kwargs']['player']`_
|
||||||
|
|
||||||
|
### pl.log -> Logger
|
||||||
|
_Константа_\
|
||||||
|
Вернёт преднастроенный логгер
|
||||||
|
|
||||||
|
### pl.addr -> str
|
||||||
|
_Константа_\
|
||||||
|
Вернёт IP адрес игрока
|
||||||
|
|
||||||
|
### pl.pid -> int
|
||||||
|
### pl.cid -> int
|
||||||
|
_Константа_\
|
||||||
|
Вернёт id клиента _(pid: PlayerId = cid: ClientId)_
|
||||||
|
|
||||||
|
### pl.key -> str
|
||||||
|
_Константа_\
|
||||||
|
Вернёт ключ, переданный при авторизации
|
||||||
|
|
||||||
|
### pl.nick -> str
|
||||||
|
_Переменная_\
|
||||||
|
Ник, переданные при авторизации от сервера BeamMP, можно изменить, последствия не проверенны
|
||||||
|
|
||||||
|
### pl.roles -> str
|
||||||
|
_Переменная_\
|
||||||
|
Роль, переданная при авторизации от сервера BeamMP, можно изменить (Если установить не верную роль, могут происходить неожиданности.)
|
||||||
|
|
||||||
|
### pl.guest -> bool
|
||||||
|
_Константа_\
|
||||||
|
Вернёт является ли игрок гостем, передаётся при авторизации от сервера BeamMP
|
||||||
|
|
||||||
|
### pl.identifiers -> dict
|
||||||
|
_Константа_\
|
||||||
|
Идентификаторы, передаются при авторизации от сервера BeamMP.
|
||||||
|
|
||||||
|
### pl.ready -> bool
|
||||||
|
_Константа, меняется ядром_\
|
||||||
|
Вернёт bool значение, если True -> игрок скачал все ресурсы, прогрузился на карте
|
||||||
|
|
||||||
|
### pl.cars -> dict
|
||||||
|
_Константа, меняется ядром_\
|
||||||
|
Возвращает словарь автомобилей по типу:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
1: {
|
||||||
|
"packet": car_packet,
|
||||||
|
"json": car_json,
|
||||||
|
"json_ok": bool(car_json),
|
||||||
|
"snowman": snowman,
|
||||||
|
"over_spawn": (snowman and allow_snowman) or over_spawn,
|
||||||
|
"pos": {
|
||||||
|
"pos":[0,0,0],
|
||||||
|
"rvel":[0,0,0],
|
||||||
|
"rot":[0,0,0],
|
||||||
|
"vel":[0,0,0],
|
||||||
|
"tim":0,
|
||||||
|
"ping":0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2: ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Где `1` - car_id\
|
||||||
|
Где `pkt` - Необработанный пакет который пришел от клиента (Для очень опытных пользователй) \
|
||||||
|
Где `json` - Обработанный пакет, хранящийся в виде dict\
|
||||||
|
Где `json_ok` - Смогло ли ядро обработать пакет\
|
||||||
|
Где `snowman` - Снеговик ли машина\
|
||||||
|
Где `over_spawn` - Заспавнена ли машина сверх лимита (Разрешается через плагины)\
|
||||||
|
Где `pos` - Позиция машины (Передаётся через udp)
|
||||||
|
|
||||||
|
### pl.last_position -> dict
|
||||||
|
_Константа, меняется ядром_
|
||||||
|
Возвращает последнюю позицию игрока
|
||||||
|
|
||||||
|
|
||||||
|
### **async** pl.kick([reason: str = "Kicked!"]) -> None
|
||||||
|
_`reason: str` -> Причина кика. Параметр не обязателен, по дефолту: `Kicked!`_
|
||||||
|
Кикает игрока с сервера
|
||||||
|
|
||||||
|
### **async** pl.send_message(message: str, [to_all: bool = True]) -> None
|
||||||
|
_`message: str` -> Текст сообщения, отправляется без "Server:"_
|
||||||
|
_`to_all: bool` -> Нужно ли отправить это сообщение всем? Параметр не обязателен, по дефолту: `True`_
|
||||||
|
Отправляет сообщение игроку или всем сразу
|
||||||
|
|
||||||
|
### **async** pl.send_event(event_name: str, event_data: Any, [to_all: bool = True]) -> None
|
||||||
|
_`event_name: str` -> Имя ивента, который будет вызван_
|
||||||
|
_`event_data: Any` -> Отправляемые данные в ивент._
|
||||||
|
_`to_all: bool` -> Нужно ли отправить это сообщение всем? Параметр не обязателен, по дефолту: `True`_
|
||||||
|
Отправляет ивент на клиент.\
|
||||||
|
Если event_data будет tuple, list, dict, то перед отправкой ядро конвертирует в json через json.dumps(event_data)
|
||||||
|
Иначе данные будут строкой, без регуляции;
|
||||||
|
|
||||||
|
### **async** pl.delete_car(self, car_id: int) -> None
|
||||||
|
_`car_id: int` -> Идентификатор машины_
|
||||||
|
Удалят машину у игрока
|
5
docs/ru/plugins/events_list.md
Normal file
5
docs/ru/plugins/events_list.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Список ивентов
|
||||||
|
|
||||||
|
В большинство ивентов будет приходить `pl = data['kwargs']['player']`, описание можно найти [тут](./classes.md)
|
||||||
|
|
||||||
|
###
|
37
docs/ru/plugins/examples/async_example.py
Normal file
37
docs/ru/plugins/examples/async_example.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
try:
|
||||||
|
import KuiToi
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
kt = KuiToi("Example")
|
||||||
|
log = kt.log
|
||||||
|
config = {"config_version": 0.1, "sql": {"enabled": False, "host": "127.0.0.1", "port": 3363, "database": "fucklua"}}
|
||||||
|
cfg_file = "config.json"
|
||||||
|
|
||||||
|
|
||||||
|
async def my_event_handler(event_data):
|
||||||
|
log.info(f"{event_data}")
|
||||||
|
|
||||||
|
|
||||||
|
async def load():
|
||||||
|
# Инициализация плагина
|
||||||
|
with open(cfg_file, 'w') as f:
|
||||||
|
json.dump(config, f)
|
||||||
|
cgf = config
|
||||||
|
log.info(cgf)
|
||||||
|
ev.register_event("my_event", my_event_handler)
|
||||||
|
log.info("Плагин загружен успешно.")
|
||||||
|
|
||||||
|
|
||||||
|
async def start():
|
||||||
|
# Запуск процессов плагина
|
||||||
|
await ev.call_async_event("my_event")
|
||||||
|
await ev.call_async_event("my_event", "Some data", data="some data too")
|
||||||
|
log.info("Плагин запустился успешно.")
|
||||||
|
|
||||||
|
|
||||||
|
async def unload():
|
||||||
|
# Код завершающий все процессы
|
||||||
|
log.info("Плагин выгружен успешно.")
|
37
docs/ru/plugins/examples/example.py
Normal file
37
docs/ru/plugins/examples/example.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
try:
|
||||||
|
import KuiToi
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
kt = KuiToi("Example")
|
||||||
|
log = kt.log
|
||||||
|
config = {"config_version": 0.1, "sql": {"enabled": False, "host": "127.0.0.1", "port": 3363, "database": "fucklua"}}
|
||||||
|
cfg_file = "config.json"
|
||||||
|
|
||||||
|
|
||||||
|
def my_event_handler(event_data):
|
||||||
|
log.info(f"{event_data}")
|
||||||
|
|
||||||
|
|
||||||
|
def load():
|
||||||
|
# Инициализация плагина
|
||||||
|
with open(cfg_file, 'w') as f:
|
||||||
|
json.dump(config, f)
|
||||||
|
cgf = config
|
||||||
|
log.info(cgf)
|
||||||
|
ev.register_event("my_event", my_event_handler)
|
||||||
|
log.info("Плагин загружен успешно.")
|
||||||
|
|
||||||
|
|
||||||
|
def start():
|
||||||
|
# Запуск процессов плагина
|
||||||
|
ev.call_event("my_event")
|
||||||
|
ev.call_event("my_event", "Some data", data="some data too")
|
||||||
|
log.info("Плагин запустился успешно.")
|
||||||
|
|
||||||
|
|
||||||
|
def unload():
|
||||||
|
# Код завершающий все процессы
|
||||||
|
log.info("Плагин выгружен успешно.")
|
27
docs/ru/plugins/lua/example.lua
Normal file
27
docs/ru/plugins/lua/example.lua
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
print("example.lua")
|
||||||
|
|
||||||
|
--CreateTimer Testing
|
||||||
|
local mytimer = MP.CreateTimer()
|
||||||
|
--.--.--.--.--.--.--.
|
||||||
|
|
||||||
|
--GetOSName Testing
|
||||||
|
print("OS Name: "..MP.GetOSName())
|
||||||
|
--.--.--.--.--.--.-
|
||||||
|
|
||||||
|
--GetServerVersion Testing
|
||||||
|
local major, minor, patch = MP.GetServerVersion()
|
||||||
|
print("Server Version: "..major.."."..minor.."."..patch)
|
||||||
|
--.--.--.--.--.--.--.--.--
|
||||||
|
|
||||||
|
--Events Testing--
|
||||||
|
function handleChat(player_id, player_name, message)
|
||||||
|
print("Lua handleChat:", player_id, player_name, message, "; Uptime: "..mytimer:GetCurrent())
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
MP.RegisterEvent("onChatMessage", "handleChat")
|
||||||
|
--.--.--.--.--.--.
|
||||||
|
|
||||||
|
function onInit()
|
||||||
|
print("Initializing ready!")
|
||||||
|
end
|
35
docs/ru/plugins/lua/readme.md
Normal file
35
docs/ru/plugins/lua/readme.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Обеспечение обратной поддержки BeamMP Lua
|
||||||
|
|
||||||
|
В KiuToi есть практически полная поддержка lua плагинов с BeamMP, все необходимые методы созданы, тестирование показало следующие нюансы:
|
||||||
|
|
||||||
|
В KiuToi не будет поддержки: `MP.Set()`
|
||||||
|
|
||||||
|
#### Economic Rework V2.0 (Платный, Discord (RU): [Hlebushek](https://discordapp.com/users/449634697593749516))
|
||||||
|
|
||||||
|
1. Для получения `pluginPath` нужно: `debug.getinfo(1).source:gsub("\\","/")` => `debug.getinfo(1).source:gsub("\\","/"):gsub("@", "")` так как пусть возвращается с `@`, что сломало плагин.
|
||||||
|
|
||||||
|
#### Cobalt Essentials V1.7.5 (Бесплатный, [github](https://github.com/prestonelam2003/CobaltEssentials/))
|
||||||
|
|
||||||
|
1. Для получения `pluginPath` нужно: `debug.getinfo(1).source:gsub("\\","/")` => `debug.getinfo(1).source:gsub("\\","/"):gsub("@", "")` так как пусть возвращается с `@`, что сломало плагин.
|
||||||
|
2. Пришлось перенести все `require()` за `onInit`
|
||||||
|
3. В некоторых моментах пришлось перенести `MP.RegisterEvent` уже после объявления функции, т.е.:
|
||||||
|
```lua
|
||||||
|
--Вот так не правильно, может не пройти регистрация
|
||||||
|
MP.RegisterEvent("onPlayerAuth","onPlayerAuth")
|
||||||
|
function onPlayerAuth(name, role, isGuest)
|
||||||
|
-- Some plugin code
|
||||||
|
end
|
||||||
|
|
||||||
|
--Вот такой вариант пройдёт
|
||||||
|
MP.RegisterEvent("onPlayerAuth","onPlayerAuth")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Немного о принципе работы
|
||||||
|
|
||||||
|
Загрузка плагина проходит в несколько этапов:
|
||||||
|
|
||||||
|
1. Сканируется папка `plugins/`
|
||||||
|
2. Если папки нет в PyPlugins и в папке есть `*.lua`, то она добавляется, допустим это будет `plugins/LuaPlugin`
|
||||||
|
3. Далее из этой папки проходит `lua.loadfile({filename})` (Это стандартный метод в lua)
|
||||||
|
4. И в конце вызывается ивент и функция `onInit()`
|
||||||
|
5. Если во время выполнения `onInit()` не произошло ошибок, можно будет увидеть через команду `lua_plugins` такое сообщение: `Lua plugins: LuaPlugin:ok`
|
@ -1,5 +1,8 @@
|
|||||||
# Система плагинов
|
# Система плагинов
|
||||||
|
|
||||||
|
### Ивенты: [тут](./events_list.md)
|
||||||
|
### Классы: [тут](./classes.md)
|
||||||
|
|
||||||
## Установка библиотеки с "Заглушками"
|
## Установка библиотеки с "Заглушками"
|
||||||
###### (Это значит, что не будет работать без сервера, но IDE подскажет API)
|
###### (Это значит, что не будет работать без сервера, но IDE подскажет API)
|
||||||
###### (Библиотека ещё в разработке)
|
###### (Библиотека ещё в разработке)
|
||||||
@ -17,7 +20,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
kt = KuiToi("Example")
|
kt = KuiToi("ExamplePlugin")
|
||||||
log = kt.log
|
log = kt.log
|
||||||
|
|
||||||
def my_event_handler(event_data):
|
def my_event_handler(event_data):
|
||||||
@ -40,6 +43,7 @@ def unload():
|
|||||||
# Код завершающий все процессы
|
# Код завершающий все процессы
|
||||||
log.info("Плагин выгружен успешно.")
|
log.info("Плагин выгружен успешно.")
|
||||||
```
|
```
|
||||||
|
Так же более обширный пример можно найти в [example.py](examples/example.py)
|
||||||
|
|
||||||
* Рекомендуется использовать `open()` после `load()`, иначе стоит использовать `kt.load()` - Создаёт файл в папке `plugin/<plugin_name>/<filename>`
|
* Рекомендуется использовать `open()` после `load()`, иначе стоит использовать `kt.load()` - Создаёт файл в папке `plugin/<plugin_name>/<filename>`
|
||||||
* Создание своего ивента : `kt.register_event("my_event", my_event_function)` -
|
* Создание своего ивента : `kt.register_event("my_event", my_event_function)` -
|
||||||
@ -84,7 +88,7 @@ async def unload():
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Так же более обширный пример можно найти в [async_example.py](./async_example.py)
|
Так же более обширный пример можно найти в [async_example.py](examples/async_example.py)
|
||||||
|
|
||||||
* Создание своего ивента: `kt.register_event("my_event", my_event_function)` (в register_event стоит проверка на функцию)
|
* Создание своего ивента: `kt.register_event("my_event", my_event_function)` (в register_event стоит проверка на функцию)
|
||||||
* Вызов async ивента: `kt.call_async_event("my_event")`
|
* Вызов async ивента: `kt.call_async_event("my_event")`
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
1. Настройка и Запуск сервера - [тута](./setup)
|
1. Настройка и Запуск сервера - [тута](./setup)
|
||||||
2. Плагины и Ивент система - [тута](./plugins)
|
2. Плагины и Ивент система - [тута](./plugins)
|
||||||
3. Мультиязычность - [тута](./multilanguage)
|
3. Ньансы работы Lua - [тута](./plugins/lua)
|
||||||
4. KuiToi WebAPI - [тута](./web)
|
4. Мультиязычность - [тута](./multilanguage)
|
||||||
5. Тут будет что-то ново....
|
5. KuiToi WebAPI - [тута](./web)
|
||||||
|
6. Тут будет что-то новое....
|
||||||
|
@ -36,19 +36,24 @@ Game:
|
|||||||
map: gridmap_v2
|
map: gridmap_v2
|
||||||
max_cars: 1
|
max_cars: 1
|
||||||
players: 8
|
players: 8
|
||||||
Server:
|
Options:
|
||||||
debug: false
|
debug: false
|
||||||
description: Welcome to KuiToi Server!
|
encoding: utf-8
|
||||||
language: en
|
language: en
|
||||||
|
log_chat: true
|
||||||
|
speed_limit: 0
|
||||||
|
use_lua: true
|
||||||
|
use_queue: false
|
||||||
|
Server:
|
||||||
|
description: Welcome to KuiToi Server!
|
||||||
name: KuiToi-Server
|
name: KuiToi-Server
|
||||||
server_ip: 0.0.0.0
|
server_ip: 0.0.0.0
|
||||||
server_port: 30813
|
server_port: 30814
|
||||||
WebAPI:
|
WebAPI:
|
||||||
enabled: false
|
enabled: false
|
||||||
secret_key: <random_key>
|
secret_key: 3838ccb03c86cdb386b67fbfdcba62d0
|
||||||
server_ip: 127.0.0.1
|
server_ip: 127.0.0.1
|
||||||
server_port: 8433
|
server_port: 8433
|
||||||
|
|
||||||
```
|
```
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
@ -62,11 +67,19 @@ WebAPI:
|
|||||||
* `max_cars` - Максимальное количество машин на игрока
|
* `max_cars` - Максимальное количество машин на игрока
|
||||||
* `players` - Максимально количество игроков
|
* `players` - Максимально количество игроков
|
||||||
|
|
||||||
### Server
|
### Options
|
||||||
|
|
||||||
* `debug` - Нужно ли выводить debug сообщения (только для опытных пользователей, немного теряется в производительности)
|
* `debug` - Нужно ли выводить debug сообщения (только для опытных пользователей, немного теряется в производительности)
|
||||||
* `description` - Описания сервера для лаунчера BeamMP
|
* `encoding` - С какой кодировкой открывать файлы
|
||||||
* `language` - С каким языком запустится сервер (Доступные на данный момент: en, ru)
|
* `language` - С каким языком запустится сервер (Доступные на данный момент: en, ru)
|
||||||
|
* `log_chat` - Нужно-ли выводить чат в консоль
|
||||||
|
* `speed_limit` - Ограничение скорости на скачивание модов (В Мб/с)
|
||||||
|
* `use_lua` - Включить ли поддержку lua
|
||||||
|
* `use_queue` - Скачивать по очереди, т.е. в один момент может скачивать только 1 клиент
|
||||||
|
|
||||||
|
### Server
|
||||||
|
|
||||||
|
* `description` - Описания сервера для лаунчера BeamMP
|
||||||
* `name` - Названия сервер для лаунчера BeamMP
|
* `name` - Названия сервер для лаунчера BeamMP
|
||||||
* `server_ip` - Какой IP адрес занять серверу (только для опытных пользователей, по умолчанию 0.0.0.0)
|
* `server_ip` - Какой IP адрес занять серверу (только для опытных пользователей, по умолчанию 0.0.0.0)
|
||||||
* `server_port` - На каком порту будет работать сервер
|
* `server_port` - На каком порту будет работать сервер
|
||||||
|
@ -54,12 +54,16 @@ class Client:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def addr(self):
|
def addr(self):
|
||||||
return self._addr
|
return self._addr[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cid(self):
|
def cid(self):
|
||||||
return self._cid
|
return self._cid
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pid(self):
|
||||||
|
return self._cid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key(self):
|
def key(self):
|
||||||
return self._key
|
return self._key
|
||||||
@ -78,7 +82,7 @@ class Client:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def cars(self):
|
def cars(self):
|
||||||
return self._cars
|
return {i: v for i, v in enumerate(self._cars) if v is not None}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_position(self):
|
def last_position(self):
|
||||||
@ -99,7 +103,11 @@ class Client:
|
|||||||
self.__alive = True
|
self.__alive = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def kick(self, reason):
|
async def kick(self, reason=None):
|
||||||
|
if not reason:
|
||||||
|
reason = "Kicked!"
|
||||||
|
else:
|
||||||
|
reason = f"{reason!r}"
|
||||||
if not self.__alive:
|
if not self.__alive:
|
||||||
self.log.debug(f"{self.nick}.kick('{reason}') skipped: Not alive;")
|
self.log.debug(f"{self.nick}.kick('{reason}') skipped: Not alive;")
|
||||||
return
|
return
|
||||||
@ -109,9 +117,19 @@ class Client:
|
|||||||
self.__alive = False
|
self.__alive = False
|
||||||
|
|
||||||
async def send_message(self, message, to_all=True):
|
async def send_message(self, message, to_all=True):
|
||||||
await self._send(f"C:{message}", to_all=to_all)
|
if not message:
|
||||||
|
message = "no message"
|
||||||
|
to_all = False
|
||||||
|
await self._send(f"C:{message!r}", to_all=to_all)
|
||||||
|
|
||||||
async def send_event(self, event_name, event_data, to_all=True):
|
async def send_event(self, event_name, event_data, to_all=True):
|
||||||
|
if isinstance(event_data, (list, tuple, dict)):
|
||||||
|
event_data = json.dumps(event_data, separators=(',', ':'))
|
||||||
|
else:
|
||||||
|
event_data = f"{event_data!r}"
|
||||||
|
if len(event_data) > 104857599:
|
||||||
|
self.log.error("Client data too big! >=104857599")
|
||||||
|
return
|
||||||
await self._send(f"E:{event_name}:{event_data}", to_all=to_all)
|
await self._send(f"E:{event_name}:{event_data}", to_all=to_all)
|
||||||
|
|
||||||
async def _send(self, data, to_all=False, to_self=True, to_udp=False, writer=None):
|
async def _send(self, data, to_all=False, to_self=True, to_udp=False, writer=None):
|
||||||
@ -351,8 +369,8 @@ class Client:
|
|||||||
|
|
||||||
async def _spawn_car(self, data):
|
async def _spawn_car(self, data):
|
||||||
car_data = data[2:]
|
car_data = data[2:]
|
||||||
car_id = next((i for i, car in enumerate(self.cars) if car is None), len(self.cars))
|
car_id = next((i for i, car in enumerate(self._cars) if car is None), len(self._cars))
|
||||||
cars_count = len(self.cars) - self.cars.count(None)
|
cars_count = len(self._cars) - self._cars.count(None)
|
||||||
if self._snowman['id'] != -1:
|
if self._snowman['id'] != -1:
|
||||||
cars_count -= 1 # -1 for unicycle
|
cars_count -= 1 # -1 for unicycle
|
||||||
self.log.debug(f"car_id={car_id}, cars_count={cars_count}")
|
self.log.debug(f"car_id={car_id}, cars_count={cars_count}")
|
||||||
@ -400,6 +418,9 @@ class Client:
|
|||||||
des = f"Od:{self.cid}-{car_id}"
|
des = f"Od:{self.cid}-{car_id}"
|
||||||
await self._send(des)
|
await self._send(des)
|
||||||
|
|
||||||
|
async def delete_car(self, car_id):
|
||||||
|
await self._delete_car(car_id=car_id)
|
||||||
|
|
||||||
async def _delete_car(self, raw_data=None, car_id=None):
|
async def _delete_car(self, raw_data=None, car_id=None):
|
||||||
|
|
||||||
if not car_id and raw_data:
|
if not car_id and raw_data:
|
||||||
@ -408,13 +429,13 @@ class Client:
|
|||||||
cid = self.cid
|
cid = self.cid
|
||||||
raw_data = f"Od:{self.cid}-{car_id}"
|
raw_data = f"Od:{self.cid}-{car_id}"
|
||||||
|
|
||||||
if car_id != -1 and self.cars[car_id]:
|
if car_id != -1 and self._cars[car_id]:
|
||||||
|
|
||||||
ev.call_lua_event("onVehicleDeleted", self.cid, car_id)
|
ev.call_lua_event("onVehicleDeleted", self.cid, car_id)
|
||||||
|
|
||||||
admin_allow = False # Delete from admin, for example...
|
admin_allow = False # Delete from admin, for example...
|
||||||
ev_data_list = ev.call_event("onCarDelete", car=self.cars[car_id], car_id=car_id, player=self)
|
ev_data_list = ev.call_event("onCarDelete", car=self._cars[car_id], car_id=car_id, player=self)
|
||||||
d2 = await ev.call_async_event("onCarDelete", car=self.cars[car_id], car_id=car_id, player=self)
|
d2 = await ev.call_async_event("onCarDelete", car=self._cars[car_id], car_id=car_id, player=self)
|
||||||
ev_data_list.extend(d2)
|
ev_data_list.extend(d2)
|
||||||
for ev_data in ev_data_list:
|
for ev_data in ev_data_list:
|
||||||
# TODO: handle event onCarDelete
|
# TODO: handle event onCarDelete
|
||||||
@ -422,7 +443,7 @@ class Client:
|
|||||||
|
|
||||||
if cid == self.cid or admin_allow:
|
if cid == self.cid or admin_allow:
|
||||||
await self._send(raw_data, to_all=True, to_self=True)
|
await self._send(raw_data, to_all=True, to_self=True)
|
||||||
car = self.cars[car_id]
|
car = self._cars[car_id]
|
||||||
if car['snowman']:
|
if car['snowman']:
|
||||||
self.log.debug(f"Snowman found")
|
self.log.debug(f"Snowman found")
|
||||||
unicycle_id = self._snowman['id']
|
unicycle_id = self._snowman['id']
|
||||||
@ -437,10 +458,10 @@ class Client:
|
|||||||
|
|
||||||
async def _edit_car(self, raw_data, data):
|
async def _edit_car(self, raw_data, data):
|
||||||
cid, car_id = self._get_cid_vid(raw_data)
|
cid, car_id = self._get_cid_vid(raw_data)
|
||||||
if car_id != -1 and self.cars[car_id]:
|
if car_id != -1 and self._cars[car_id]:
|
||||||
client = self.__Core.get_client(cid=cid)
|
client = self.__Core.get_client(cid=cid)
|
||||||
if client:
|
if client:
|
||||||
car = client.cars[car_id]
|
car = client._cars[car_id]
|
||||||
new_car_json = {}
|
new_car_json = {}
|
||||||
try:
|
try:
|
||||||
new_car_json = json.loads(data[data.find("{"):])
|
new_car_json = json.loads(data[data.find("{"):])
|
||||||
@ -478,7 +499,7 @@ class Client:
|
|||||||
|
|
||||||
async def _reset_car(self, raw_data):
|
async def _reset_car(self, raw_data):
|
||||||
cid, car_id = self._get_cid_vid(raw_data)
|
cid, car_id = self._get_cid_vid(raw_data)
|
||||||
if car_id != -1 and cid == self.cid and self.cars[car_id]:
|
if car_id != -1 and cid == self.cid and self._cars[car_id]:
|
||||||
await self._send(raw_data, to_all=True, to_self=False)
|
await self._send(raw_data, to_all=True, to_self=False)
|
||||||
ev.call_lua_event("onVehicleReset", self.cid, car_id, raw_data[raw_data.find("{"):])
|
ev.call_lua_event("onVehicleReset", self.cid, car_id, raw_data[raw_data.find("{"):])
|
||||||
car_json = {}
|
car_json = {}
|
||||||
@ -531,16 +552,17 @@ class Client:
|
|||||||
|
|
||||||
await self._send(f"Sn{self.nick}", to_all=True) # I don't know for what it
|
await self._send(f"Sn{self.nick}", to_all=True) # I don't know for what it
|
||||||
await self._send(f"JWelcome {self.nick}!", to_all=True) # Hello message
|
await self._send(f"JWelcome {self.nick}!", to_all=True) # Hello message
|
||||||
self._ready = True
|
|
||||||
|
|
||||||
for client in self.__Core.clients:
|
for client in self.__Core.clients:
|
||||||
if not client:
|
if not client:
|
||||||
continue
|
continue
|
||||||
for car in client.cars:
|
for car in client._cars:
|
||||||
if not car:
|
if not car:
|
||||||
continue
|
continue
|
||||||
await self._send(car['packet'])
|
await self._send(car['packet'])
|
||||||
|
|
||||||
|
self._ready = True
|
||||||
|
|
||||||
async def _chat_handler(self, data):
|
async def _chat_handler(self, data):
|
||||||
sup = data.find(":", 2)
|
sup = data.find(":", 2)
|
||||||
if sup == -1:
|
if sup == -1:
|
||||||
@ -643,7 +665,7 @@ class Client:
|
|||||||
self.__alive = False
|
self.__alive = False
|
||||||
if (self.cid > 0 or self.nick is not None) and \
|
if (self.cid > 0 or self.nick is not None) and \
|
||||||
self.__Core.clients_by_nick.get(self.nick):
|
self.__Core.clients_by_nick.get(self.nick):
|
||||||
for i, car in enumerate(self.cars):
|
for i, car in enumerate(self._cars):
|
||||||
if not car:
|
if not car:
|
||||||
continue
|
continue
|
||||||
self.log.debug(f"Removing car: car_id={i}")
|
self.log.debug(f"Removing car: car_id={i}")
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from asyncio import StreamReader, StreamWriter, DatagramTransport
|
from asyncio import StreamReader, StreamWriter, DatagramTransport
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
from typing import Tuple, List, Dict, Optional, Union
|
from typing import Tuple, List, Dict, Optional, Union, Any
|
||||||
|
|
||||||
from core import Core, utils
|
from core import Core, utils
|
||||||
|
|
||||||
@ -46,6 +46,7 @@ class Client:
|
|||||||
def addr(self) -> Tuple[str, int]: ...
|
def addr(self) -> Tuple[str, int]: ...
|
||||||
@property
|
@property
|
||||||
def cid(self) -> int: ...
|
def cid(self) -> int: ...
|
||||||
|
def pid(self) -> int: ...
|
||||||
@property
|
@property
|
||||||
def key(self) -> str: ...
|
def key(self) -> str: ...
|
||||||
@property
|
@property
|
||||||
@ -55,20 +56,21 @@ class Client:
|
|||||||
@property
|
@property
|
||||||
def identifiers(self) -> list: ...
|
def identifiers(self) -> list: ...
|
||||||
@property
|
@property
|
||||||
def cars(self) -> List[dict | None]: ...
|
def cars(self) -> dict: ...
|
||||||
@property
|
@property
|
||||||
def last_position(self): ...
|
def last_position(self) -> dict: ...
|
||||||
def is_disconnected(self) -> bool: ...
|
def is_disconnected(self) -> bool: ...
|
||||||
async def kick(self, reason: str) -> None: ...
|
async def kick(self, reason: str) -> None: ...
|
||||||
async def send_message(self, message: str | bytes, to_all: bool = True) -> None:...
|
async def send_message(self, message: str | bytes, to_all: bool = True) -> None:...
|
||||||
async def send_event(self, event_name: str, event_data: str) -> None: ...
|
async def send_event(self, event_name: str, event_data: Any, to_all: bool = True) -> None: ...
|
||||||
async def _send(self, data: bytes | str, to_all: bool = False, to_self: bool = True, to_udp: bool = False, writer: StreamWriter = None) -> None: ...
|
async def _send(self, data: bytes | str, to_all: bool = False, to_self: bool = True, to_udp: bool = False, writer: StreamWriter = None) -> None: ...
|
||||||
async def _sync_resources(self) -> None: ...
|
async def _sync_resources(self) -> None: ...
|
||||||
async def _recv(self, one=False) -> bytes | None: ...
|
async def _recv(self, one=False) -> bytes | None: ...
|
||||||
async def _split_load(self, start: int, end: int, d_sock: bool, filename: str, sl: float) -> None: ...
|
async def _split_load(self, start: int, end: int, d_sock: bool, filename: str, sl: float) -> None: ...
|
||||||
async def _get_cid_vid(self, s: str) -> Tuple[int, int]: ...
|
async def _get_cid_vid(self, s: str) -> Tuple[int, int]: ...
|
||||||
async def _spawn_car(self, data: str) -> None: ...
|
async def _spawn_car(self, data: str) -> None: ...
|
||||||
async def _delete_car(self, raw_data: str = None, car_id : int =None) -> None: ...
|
async def delete_car(self, car_id: int) -> None: ...
|
||||||
|
async def _delete_car(self, raw_data: str = None, car_id: int = None) -> None: ...
|
||||||
async def _edit_car(self, raw_data: str, data: str) -> None: ...
|
async def _edit_car(self, raw_data: str, data: str) -> None: ...
|
||||||
async def _reset_car(self, raw_data: str) -> None: ...
|
async def _reset_car(self, raw_data: str) -> None: ...
|
||||||
async def _handle_car_codes(self, data: str) -> None: ...
|
async def _handle_car_codes(self, data: str) -> None: ...
|
||||||
|
@ -50,6 +50,8 @@ class Core:
|
|||||||
ev.register_event("_get_player", lambda x: self.get_client(**x['kwargs']))
|
ev.register_event("_get_player", lambda x: self.get_client(**x['kwargs']))
|
||||||
|
|
||||||
def get_client(self, cid=None, nick=None):
|
def get_client(self, cid=None, nick=None):
|
||||||
|
if cid is None and nick is None:
|
||||||
|
return None
|
||||||
if cid is not None:
|
if cid is not None:
|
||||||
if cid == -1:
|
if cid == -1:
|
||||||
return [i for i in self.clients if i is not None]
|
return [i for i in self.clients if i is not None]
|
||||||
|
@ -6,7 +6,6 @@ import random
|
|||||||
import shutil
|
import shutil
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
from lupa.lua53 import LuaRuntime
|
from lupa.lua53 import LuaRuntime
|
||||||
@ -186,7 +185,7 @@ class MP:
|
|||||||
return self._lua.table(), "Bad client"
|
return self._lua.table(), "Bad client"
|
||||||
client = ev.call_event("_get_player", cid=player_id)[0]
|
client = ev.call_event("_get_player", cid=player_id)[0]
|
||||||
if client:
|
if client:
|
||||||
car = client.cars[car_id]
|
car = client._cars[car_id]
|
||||||
if car:
|
if car:
|
||||||
return self._lua.table_from(car['pos'])
|
return self._lua.table_from(car['pos'])
|
||||||
return self._lua.table(), "Vehicle not found"
|
return self._lua.table(), "Vehicle not found"
|
||||||
@ -231,7 +230,7 @@ class MP:
|
|||||||
return self._lua.table()
|
return self._lua.table()
|
||||||
client = ev.call_event("_get_player", cid=player_id)[0]
|
client = ev.call_event("_get_player", cid=player_id)[0]
|
||||||
if client:
|
if client:
|
||||||
return self._lua.table_from([f'{v["json"]}' for d in [i for i in client.cars if i is not None]
|
return self._lua.table_from([f'{v["json"]}' for d in [i for i in client._cars if i is not None]
|
||||||
for k, v in d.items() if k == "json"])
|
for k, v in d.items() if k == "json"])
|
||||||
|
|
||||||
def GetPlayers(self):
|
def GetPlayers(self):
|
||||||
|
@ -16,19 +16,22 @@ from threading import Thread
|
|||||||
from core import get_logger
|
from core import get_logger
|
||||||
|
|
||||||
|
|
||||||
# TODO: call_client_event, get_player, get_players, GetPlayerCount
|
|
||||||
class KuiToi:
|
class KuiToi:
|
||||||
_plugins_dir = ""
|
_plugins_dir = ""
|
||||||
|
|
||||||
def __init__(self, name=None):
|
def __init__(self, name):
|
||||||
if name is None:
|
if name is None:
|
||||||
raise AttributeError("KuiToi: Name is required")
|
raise AttributeError("KuiToi: Name is required")
|
||||||
self.log = get_logger(f"Plugin | {name}")
|
self.__log = get_logger(f"Plugin | {name}")
|
||||||
self.__name = name
|
self.__name = name
|
||||||
self.__dir = os.path.join(self._plugins_dir, self.__name)
|
self.__dir = os.path.join(self._plugins_dir, self.__name)
|
||||||
if not os.path.exists(self.__dir):
|
if not os.path.exists(self.__dir):
|
||||||
os.mkdir(self.__dir)
|
os.mkdir(self.__dir)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def log(self):
|
||||||
|
return self.__log
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self.__name
|
return self.__name
|
||||||
@ -41,6 +44,7 @@ class KuiToi:
|
|||||||
def open(self, file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None):
|
def open(self, file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None):
|
||||||
path = os.path.join(self.__dir, file)
|
path = os.path.join(self.__dir, file)
|
||||||
self.log.debug(f'Trying to open "{path}" with mode "{mode}"')
|
self.log.debug(f'Trying to open "{path}" with mode "{mode}"')
|
||||||
|
# Really need?
|
||||||
# if not os.path.exists(path):
|
# if not os.path.exists(path):
|
||||||
# with open(path, 'x'): ...
|
# with open(path, 'x'): ...
|
||||||
f = None
|
f = None
|
||||||
@ -57,9 +61,35 @@ class KuiToi:
|
|||||||
self.log.debug(f"Registering event {event_name}")
|
self.log.debug(f"Registering event {event_name}")
|
||||||
ev.register_event(event_name, event_func)
|
ev.register_event(event_name, event_func)
|
||||||
|
|
||||||
def call_event(self, event_name, *data, **kwargs):
|
def call_event(self, event_name, *args, **kwargs):
|
||||||
self.log.debug(f"Called event {event_name}")
|
self.log.debug(f"Called event {event_name}")
|
||||||
ev.call_event(event_name, *data, **kwargs)
|
return ev.call_event(event_name, *args, **kwargs)
|
||||||
|
|
||||||
|
async def call_async_event(self, event_name, *args, **kwargs):
|
||||||
|
self.log.debug(f"Called async event {event_name}")
|
||||||
|
return await ev.call_async_event(event_name, *args, **kwargs)
|
||||||
|
|
||||||
|
def call_lua_event(self, event_name, *args):
|
||||||
|
self.log.debug(f"Called lua event {event_name}")
|
||||||
|
return ev.call_lua_event(event_name, *args)
|
||||||
|
|
||||||
|
def get_player(self, pid=None, nick=None, cid=None):
|
||||||
|
self.log.debug("Requests get_player")
|
||||||
|
return ev.call_event("_get_player", cid=cid or pid, nick=nick)[0]
|
||||||
|
|
||||||
|
def get_players(self):
|
||||||
|
self.log.debug("Requests get_players")
|
||||||
|
return self.get_player(-1)
|
||||||
|
|
||||||
|
def players_counter(self):
|
||||||
|
self.log.debug("Requests players_counter")
|
||||||
|
return len(self.get_players())
|
||||||
|
|
||||||
|
def is_player_connected(self, pid=None, nick=None):
|
||||||
|
self.log.debug("Requests is_player_connected")
|
||||||
|
if pid < 0:
|
||||||
|
return False
|
||||||
|
return bool(self.get_player(cid=pid, nick=nick))
|
||||||
|
|
||||||
|
|
||||||
class PluginsLoader:
|
class PluginsLoader:
|
||||||
|
48
src/modules/i18n/files/cn.json
Normal file
48
src/modules/i18n/files/cn.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"": "基础阶段",
|
||||||
|
"hello": "来自KuiToi服务器的问候!",
|
||||||
|
"config_path": "使用{}进行配置。",
|
||||||
|
"init_ok": "初始化完成。",
|
||||||
|
"start": "服务器已启动!",
|
||||||
|
"stop": "服务器已停止!",
|
||||||
|
|
||||||
|
"": "服务器认证",
|
||||||
|
"auth_need_key": "需要BeamMP密钥才能启动!",
|
||||||
|
"auth_empty_key": "BeamMP密钥为空!",
|
||||||
|
"auth_cannot_open_browser": "无法打开浏览器:{}",
|
||||||
|
"auth_use_link": "使用此链接:{}",
|
||||||
|
|
||||||
|
"": "GUI阶段",
|
||||||
|
"GUI_yes": "是",
|
||||||
|
"GUI_no": "否",
|
||||||
|
"GUI_ok": "确定",
|
||||||
|
"GUI_cancel": "取消",
|
||||||
|
"GUI_need_key_message": "需要BeamMP密钥才能启动!\n是否在浏览器中打开链接以获取密钥?",
|
||||||
|
"GUI_enter_key_message": "请输入密钥:",
|
||||||
|
"GUI_cannot_open_browser": "无法打开浏览器。\n请使用此链接:{}",
|
||||||
|
|
||||||
|
"": "Web阶段",
|
||||||
|
"web_start": "WebAPI已启动{}(CTRL+C停止)",
|
||||||
|
|
||||||
|
"": "命令:man",
|
||||||
|
"man_message_man": "man - 显示COMMAND的帮助页面。\n用法:man COMMAND",
|
||||||
|
"help_message_man": "显示COMMAND的帮助页面。",
|
||||||
|
"man_for": "帮助页面",
|
||||||
|
"man_message_not_found": "man:找不到帮助页面。",
|
||||||
|
"man_command_not_found": "man:找不到\"{}\"命令!",
|
||||||
|
|
||||||
|
"": "命令:help",
|
||||||
|
"man_message_help": "help - 显示命令的名称和简短描述。\n用法:help [--raw]\n命令`help`列出所有可用的命令,并为每个命令提供简短描述。",
|
||||||
|
"help_message_help": "显示命令的名称和简短描述。",
|
||||||
|
"help_command": "命令",
|
||||||
|
"help_message": "文本",
|
||||||
|
"help_message_not_found": "无文本",
|
||||||
|
|
||||||
|
"": "命令:stop",
|
||||||
|
"man_message_stop": "stop - 关闭服务器。\n用法:stop",
|
||||||
|
"help_message_stop": "关闭服务器。",
|
||||||
|
|
||||||
|
"": "命令:exit",
|
||||||
|
"man_message_exit": "exit - 关闭服务器。\n用法:exit",
|
||||||
|
"help_message_exit": "关闭服务器。"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user