Compare commits

...

20 Commits

Author SHA1 Message Date
bf0a3f3feb Update TODOs; 2023-07-22 07:56:55 +03:00
5e071c5705 EventTimer Ready;
Unload lua plugins;
2023-07-22 07:56:23 +03:00
132beb0dd6 Add MP.CreateTimer(), MP.CreateEventTimer(), MP.CancelEventTimer() 2023-07-22 07:48:35 +03:00
33f2d2ba72 Add call_lua_events() 2023-07-22 06:28:08 +03:00
90113179d7 Add ServerConfig.toml generation; 2023-07-22 06:00:48 +03:00
cd178b815a Add log_chat; 2023-07-22 05:49:52 +03:00
c1f3983856 Minor 2023-07-22 05:23:56 +03:00
96cc4b08db Add warnings to lua;
Add MP.Settings;
2023-07-22 05:23:02 +03:00
d13a319f39 Remove LuaPluginsLoader from imports;
Add use_lua to config;
2023-07-22 05:05:15 +03:00
f24ae23eac Some fixes; 2023-07-22 05:01:11 +03:00
b31b01d137 Update build 2023-07-22 05:00:44 +03:00
e7be3c88be Configs 2023-07-22 04:44:23 +03:00
2dd8b5f5eb _event_waiters;
Recreate loading lua plugins;
2023-07-22 04:16:16 +03:00
84c45d321a Minor update; 2023-07-22 03:51:58 +03:00
b1162af681 Update TODOs 2023-07-22 02:53:28 +03:00
91c9cd8454 CreateTimer 2023-07-22 02:52:57 +03:00
b8326ecdf8 Refactored for pretty view error; 2023-07-22 02:49:13 +03:00
c068629c83 Add calling lua events 2023-07-22 02:48:42 +03:00
905c0a361d Add GetPlayerIDByName (Non docs...); 2023-07-22 02:48:23 +03:00
d7073d9124 New print (Now print tables as json);
Fix _recursive_dict_encode;
Fix lua.globals().onInit();
2023-07-22 01:48:55 +03:00
14 changed files with 350 additions and 111 deletions

4
.gitignore vendored
View File

@ -137,4 +137,6 @@ dmypy.json
/src/plugins
/test/
*test.py
logs/
logs/
*.yml
*.toml

View File

@ -19,7 +19,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
- [x] Chat
- [x] Players online counter
- [x] Packets handled (Recursive finding second packet)
- [ ] Client events
- [x] Client events
- [x] Car synchronizations:
- [x] State packets
- [x] Spawn cars
@ -63,7 +63,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
- [ ] Client (Player) class
- [x] Lua part: (Original BeamMP compatibility)
- [x] Load Lua plugins
- [x] MP Class (Excluding CreateEventTimer, CreateEventTimer)
- [x] MP Class
- [x] Util class
- [x] FS class
- [x] MultiLanguage (i18n support)

View File

@ -6,4 +6,5 @@ fastapi~=0.100.0
starlette~=0.27.0
pydantic~=2.0.2
click~=8.1.4
lupa~=2.0
lupa~=2.0
toml~=0.10.2

View File

@ -364,6 +364,9 @@ class Client:
allow = True
allow_snowman = True
over_spawn = False
lua_data = ev.call_lua_event("onVehicleSpawn", self.cid, car_id, car_data[car_data.find("{"):])
if 1 in lua_data:
allow = False
ev_data_list = ev.call_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
d2 = await ev.call_async_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
ev_data_list.extend(d2)
@ -407,6 +410,8 @@ class Client:
if car_id != -1 and self.cars[car_id]:
ev.call_lua_event("onVehicleDeleted", self.cid, car_id)
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)
d2 = await ev.call_async_event("onCarDelete", car=self.cars[car_id], car_id=car_id, player=self)
@ -444,6 +449,9 @@ class Client:
allow = False
admin_allow = False
lua_data = ev.call_lua_event("onVehicleEdited", self.cid, car_id, data[data.find("{"):])
if 1 in lua_data:
allow = False
ev_data_list = ev.call_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
d2 = await ev.call_async_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
ev_data_list.extend(d2)
@ -472,8 +480,14 @@ class Client:
cid, car_id = self._get_cid_vid(raw_data)
if car_id != -1 and cid == self.cid and self.cars[car_id]:
await self._send(raw_data, to_all=True, to_self=False)
ev.call_event("onCarReset", car=self.cars[car_id], car_id=car_id, player=self)
await ev.call_async_event("onCarReset", car=self.cars[car_id], car_id=car_id, player=self)
ev.call_lua_event("onVehicleReset", self.cid, car_id, raw_data[raw_data.find("{"):])
car_json = {}
try:
car_json = json.loads(raw_data[raw_data.find("{"):])
except Exception as e:
self.log.debug(f"Invalid new_car_json: Error: {e}; Data: {raw_data}")
ev.call_event("onCarReset", car=car_json, car_id=car_id, player=self)
await ev.call_async_event("onCarReset", car=car_json, car_id=car_id, player=self)
self.log.debug(f"Car reset: car_id={car_id}")
else:
self.log.debug(f"Invalid car: car_id={car_id}")
@ -536,7 +550,11 @@ class Client:
self.log.debug("Tried to send an empty event, ignoring")
return
to_ev = {"message": msg, "player": self}
ev.call_lua_event("onChatMessage", self.cid, self.nick, msg)
lua_data = ev.call_lua_event("onChatMessage", self.cid, self.nick, msg)
if 1 in lua_data:
if config.Options['log_chat']:
self.log.info(f"{self.nick}: {msg}")
return
ev_data_list = ev.call_event("onChatReceive", **to_ev)
d2 = await ev.call_async_event("onChatReceive", **to_ev)
ev_data_list.extend(d2)
@ -555,13 +573,15 @@ class Client:
if to_client:
# noinspection PyProtectedMember
writer = to_client._writer
self.log.info(f"{message}" if to_all else f"{self.nick}: {msg}")
if config.Options['log_chat']:
self.log.info(f"{message}" if to_all else f"{self.nick}: {msg}")
await self._send(f"C:{message}", to_all=to_all, to_self=to_self, writer=writer)
need_send = False
except KeyError | AttributeError:
self.log.error(f"Returns invalid data: {ev_data}")
if need_send:
self.log.info(f"{self.nick}: {msg}")
if config.Options['log_chat']:
self.log.info(f"{self.nick}: {msg}")
await self._send(data, to_all=True)
async def _handle_codes(self, data):
@ -599,9 +619,11 @@ class Client:
await self._send(data, to_all=True, to_self=False)
async def _looper(self):
ev.call_lua_event("onPlayerConnecting", self.cid)
self._connect_time = time.monotonic()
await self._send(f"P{self.cid}") # Send clientID
await self._sync_resources()
ev.call_lua_event("onPlayerJoining", self.cid)
tasks = self.__tasks
recv = asyncio.create_task(self._recv())
tasks.append(recv)
@ -629,6 +651,10 @@ class Client:
if self.ready:
await self._send(f"J{self.nick} disconnected!", to_all=True, to_self=False) # I'm disconnected.
self.log.debug(f"Removing client")
ev.call_lua_event("onPlayerDisconnect", self.cid)
ev.call_event("onPlayerDisconnect", player=self)
await ev.call_async_event("onPlayerDisconnect", player=self)
# TODO: i18n
self.log.info(f"Disconnected, online time: {round((time.monotonic() - self._connect_time) / 60, 2)}min.")
self.__Core.clients[self.cid] = None

View File

@ -11,7 +11,7 @@ __title__ = 'KuiToi-Server'
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
__version__ = '0.4.1'
__build__ = 1486 # Я это считаю лог файлами
__build__ = 1925 # Я это считаю лог файлами
__author__ = 'SantaSpeen'
__author_email__ = 'admin@kuitoi.su'
__license__ = "FPA"

View File

@ -16,7 +16,7 @@ from core import utils
from core.Client import Client
from core.tcp_server import TCPServer
from core.udp_server import UDPServer
from modules import PluginsLoader, LuaPluginsLoader
from modules import PluginsLoader
from modules.WebAPISystem import app as webapp
@ -224,8 +224,10 @@ class Core:
os.mkdir(pl_dir)
pl = PluginsLoader(pl_dir)
await pl.load()
lpl = LuaPluginsLoader(pl_dir)
lpl.load()
if config.Options['use_lua']:
from modules.PluginsLoader.lua_plugins_loader import LuaPluginsLoader
lpl = LuaPluginsLoader(pl_dir)
lpl.load()
try:
# WebApi Start

View File

@ -73,6 +73,20 @@ class TCPServer:
await client.kick('Stale Client (replaced by new client)')
return False, client
allow = True
reason = "You are not allowed on the server!"
lua_data = ev.call_lua_event("onPlayerAuth", client.nick, client.roles, client.guest, client.identifiers)
for data in lua_data:
if 1 == data:
allow = True
elif isinstance(data, str):
allow = True
reason = data
if not allow:
await client.kick(reason)
return False, client
ev.call_event("onPlayerAuthenticated", player=client)
if len(self.Core.clients_by_id) > config.Game["players"]:

View File

@ -10,6 +10,7 @@ import secrets
import yaml
class Config:
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
self.Auth = auth or {"key": None, "private": True}
@ -17,7 +18,7 @@ class Config:
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
"server_ip": "0.0.0.0", "server_port": 30814}
self.Options = options or {"language": "en", "encoding": "utf-8", "speed_limit": 0, "use_queue": False,
"debug": False}
"debug": False, "use_lua": False, "log_chat": True}
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
"secret_key": secrets.token_hex(16)}

View File

@ -33,6 +33,7 @@ class EventsSystem:
"onCarReset": [],
"onSentPing": [], # Only sync
"onChangePosition": [], # Only sync
"onPlayerDisconnect": [],
"onServerStopped": [],
}
self.__async_events = {
@ -43,6 +44,7 @@ class EventsSystem:
"onCarDelete": [],
"onCarEdited": [],
"onCarReset": [],
"onPlayerDisconnect": [],
"onServerStopped": []
}
@ -71,9 +73,9 @@ class EventsSystem:
f"async_event={async_event}, lua_event={lua}):")
if lua:
if event_name not in self.__lua_events:
self.__lua_events.update({str(event_name): [event_func]})
self.__lua_events.update({str(event_name): [{"func": event_func, "name": lua}]})
else:
self.__lua_events[event_name].append(event_func)
self.__lua_events[event_name].append({"func": event_func, "name": lua})
self.log.debug("Register ok")
return
@ -138,12 +140,14 @@ class EventsSystem:
self.log.debug(f"Calling lua event: '{event_name}'")
funcs_data = []
if event_name in self.__lua_events.keys():
for func in self.__lua_events[event_name]:
for data in self.__lua_events[event_name]:
func = data['func']
try:
funcs_data.append(func(*args))
fd = func(*args)
funcs_data.append(fd)
except Exception as e:
# TODO: i18n
self.log.error(f'Error while calling "{event_name}"; In function: "{func.__name__}"')
self.log.error(f'Error while calling lua event "{event_name}"; In function: "{data["name"]}"')
self.log.exception(e)
else:
# TODO: i18n

View File

@ -1,2 +1 @@
from .plugins_loader import PluginsLoader
from .lua_plugins_loader import LuaPluginsLoader

View File

@ -1,26 +1,100 @@
package.path = package.path..";modules/PluginsLoader/lua_libs/?.lua"
MP.Timer = {}
function MP.CreateTimer()
local newObj = {
startTime = os.clock()
}
setmetatable(newObj, { __index = MP.Timer })
return newObj
end
function MP.Timer:GetCurrent()
return os.clock() - self.startTime
end
function MP.Timer:Start()
self.startTime = os.clock()
end
function MP.Sleep(time_ms)
local start = getTickCount()
while getTickCount() - start < time_ms do end
end
MP.CallStrategy = {
BestEffort = 0,
Precise = 1
}
MP.Settings = {
Debug = 0,
Private = 1,
MaxCars = 2,
MaxPlayers = 3,
Map = 4,
Name = 5,
Description = 6
}
function MP.CreateTimer()
MP.log.debug("request MP.CreateTimer()")
local timer = {}
timer.start_time = os.clock()
function timer:GetCurrent()
return os.clock() - self.start_time
end
function timer:Start()
self.start_time = os.clock()
end
return timer
end
----Timer object for event timers
--local TimedEvent = {}
--TimedEvent.__index = TimedEvent
--
--function TimedEvent:new(interval_ms, event_name, strategy)
-- local o = {}
-- setmetatable(o, self)
-- o.interval = interval_ms
-- o.event_name = event_name
-- o.strategy = strategy or MP.CallStrategy.BestEffort
-- o.last_trigger_time = 0
-- o.timer = MP.CreateTimer()
-- return o
--end
--
--function TimedEvent:trigger()
-- MP.TriggerLocalEvent(self.event_name)
-- self.last_trigger_time = self.timer:GetCurrent()
--end
--
--function TimedEvent:is_ready()
-- local elapsed_time = self.timer:GetCurrent() - self.last_trigger_time
-- return elapsed_time * 1000 >= self.interval
--end
--
---- Event timer management functions
--MP.event_timers = {}
--MP.event_timers_mutex = {}
--
--function MP.CreateEventTimer(event_name, interval_ms, strategy)
-- MP.log.debug("request MP.CreateEventTimer()")
-- strategy = strategy or MP.CallStrategy.BestEffort
-- local timer = TimedEvent:new(interval_ms, event_name, strategy)
-- table.insert(MP.event_timers, timer)
-- MP.log.debug("created event timer for \"" .. event_name .. "\" with " .. interval_ms .. "ms interval")
--end
--
--function MP.CancelEventTimer(event_name)
-- MP.log.debug("request MP.CancelEventTimer()")
-- for i, timer in ipairs(MP.event_timers) do
-- if timer.event_name == event_name then
-- table.remove(MP.event_timers, i)
-- end
-- end
-- MP.log.debug("cancelled event timer for \"" .. event_name .. "\"")
--end
--
--function MP.run_event_timers()
-- MP.log.debug("request MP.run_event_timers()")
-- while true do
-- -- Wait for some time before checking timers
-- MP.Sleep(100)
--
-- -- Check each timer and trigger events as necessary
-- for _, timer in ipairs(MP.event_timers) do
-- if timer:is_ready() then
-- if timer.strategy == MP.CallStrategy.Precise then
-- while timer:is_ready() do
-- timer:trigger()
-- end
-- else
-- timer:trigger()
-- end
-- end
-- end
-- end
--end

View File

@ -4,21 +4,53 @@ import os
import platform
import random
import shutil
import threading
import time
from threading import Thread
import toml
from lupa.lua53 import LuaRuntime
from core import get_logger
class EventTimer:
def __init__(self, event_name, interval_ms, mp, strategy=None):
self.log = get_logger(f"EventTimer | {mp.name}")
self.mp = mp
self.event_name = event_name
self.interval_ms = interval_ms
self.strategy = strategy
self.timer = None
self.stopped = False
def start(self):
def callback():
if not self.stopped:
self.start()
self.trigger_event()
self.timer = threading.Timer(self.interval_ms / 1000.0, callback)
self.timer.start()
def stop(self):
self.stopped = True
if self.timer is not None:
self.timer.cancel()
def trigger_event(self):
self.log.debug(f"Event '{self.event_name}' triggered")
self.mp.TriggerLocalEvent(self.event_name)
# noinspection PyPep8Naming
class MP:
# In ./in_lua.lua
# MP.CreateTimer
# MP.Sleep
def __init__(self, name: str, lua: LuaRuntime):
self.loaded = False
self._event_waiters = []
self.loop = asyncio.get_event_loop()
self.log = get_logger(f"LuaPlugin | {name}")
self.name = name
@ -29,8 +61,13 @@ class MP:
"onPlayerJoin": [], "onPlayerDisconnect": [], "onChatMessage": [], "onVehicleSpawn": [],
"onVehicleEdited": [], "onVehicleDeleted": [], "onVehicleReset": [], "onFileChanged": []
}
self._event_timers = {}
def _print(self, *args):
args = list(args)
for i, arg in enumerate(args):
if "LuaTable" in str(type(arg)):
args[i] = self._lua.globals().Util.JsonEncode(arg)
s = " ".join(map(str, args))
self.log.info(s)
@ -45,10 +82,21 @@ class MP:
self.log.debug("request MP.GetServerVersion()")
return ev.call_event("_get_BeamMP_version")[0]
def _reg_ev(self):
for event in self._event_waiters:
self.RegisterEvent(*event)
def RegisterEvent(self, event_name: str, function_name: str) -> None:
self.log.debug("request MP.RegisterEvent()")
if not self.loaded:
self.log.debug("MP.RegisterEvent: plugin not loaded, waiting...")
self._event_waiters.append([event_name, function_name])
return
event_func = self._lua.globals()[function_name]
ev.register_event(event_name, event_func, lua=True)
if not event_func:
self.log.warning(f"Can't register '{event_name}': not found function: '{function_name}'")
return
ev.register_event(event_name, event_func, lua=function_name)
if event_name not in self._local_events:
self._local_events.update({str(event_name): [event_func]})
else:
@ -57,36 +105,45 @@ class MP:
def CreateEventTimer(self, event_name: str, interval_ms: int, strategy: int = None):
self.log.debug("request CreateEventTimer()")
# TODO: CreateEventTimer
event_timer = EventTimer(event_name, interval_ms, self, strategy)
self._event_timers[event_name] = event_timer
event_timer.start()
def CancelEventTimer(self, event_name: str):
self.log.debug("request CancelEventTimer()")
# TODO: CancelEventTimer
if event_name in self._event_timers:
event_timer = self._event_timers[event_name]
event_timer.stop()
del self._event_timers[event_name]
def TriggerLocalEvent(self, event_name, *args):
self.log.debug("request TriggerLocalEvent()")
self.log.debug(f"Calling lcoal lua event: '{event_name}'")
self.log.debug(f"Calling local lua event: '{event_name}'")
funcs_data = []
if event_name in self._local_events.keys():
for func in self._local_events[event_name]:
try:
funcs_data.append(func(*args))
except Exception as e:
self.log.error(f'Error while calling "{event_name}"; In function: "{func.__name__}"')
self.log.error(f'Error while calling "{event_name}"; In function: "{func}"')
self.log.exception(e)
else:
self.log.warning(f"Event {event_name} does not exist, maybe ev.call_lua_event() or MP.Trigger<>Event()?. "
f"Just skipping it...")
return self._lua.table_from({i: v for i, v in enumerate(funcs_data)})
return self._lua.table_from(funcs_data)
def TriggerGlobalEvent(self, event_name, *args):
self.log.debug("request TriggerGlobalEvent()")
return self._lua.table(
IsDone=lambda: True,
GetResults=lambda: self._lua.table_from({i: v for i, v in enumerate(ev.call_lua_event(event_name, *args))})
GetResults=lambda: self._lua.table_from(ev.call_lua_event(event_name, *args))
)
def Sleep(self, time_ms):
self.log.debug(f"request Sleep(); Thread: {threading.current_thread().name}")
time.sleep(time_ms * 0.001)
def SendChatMessage(self, player_id, message):
self.log.debug("request SendChatMessage()")
client = ev.call_event("_get_player", cid=player_id)[0]
@ -150,6 +207,15 @@ class MP:
return client.nick
return
def GetPlayerIDByName(self, player_name):
self.log.debug("request GetPlayerIDByName()")
if not isinstance(player_name, str):
return None
client = ev.call_event("_get_player", nick=player_name)[0]
if client:
return client.cid
return
def RemoveVehicle(self, player_id, vehicle_id):
self.log.debug("request RemoveVehicle()")
if player_id < 0:
@ -165,14 +231,13 @@ class MP:
return self._lua.table()
client = ev.call_event("_get_player", cid=player_id)[0]
if client:
return self._lua.table_from(
{i: f'{v["json"]}' for i, d in enumerate([i for i in client.cars if i is not None]) for k, v in
d.items() if k == "json"})
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"])
def GetPlayers(self):
self.log.debug("request GetPlayers()")
clients = ev.call_event("_get_players", cid=-1)
return self._lua.table_from({i: n for i, n in enumerate(clients)})
return self._lua.table_from(clients)
def IsPlayerGuest(self, player_id) -> bool:
self.log.debug("request IsPlayerGuest()")
@ -212,10 +277,6 @@ class MP:
self.log.debug("request Set")
self.log.warning("KuiToi cannot support this: MP.Set()")
def Settings(self, *args):
self.log.debug("request Set")
self.log.warning("KuiToi cannot support this: MP.Settings()")
# noinspection PyPep8Naming
class Util:
@ -240,18 +301,19 @@ class Util:
return [i for i in new_list if i is not None]
def _recursive_dict_encode(self, table):
new_dict = dict(table)
for k, v in table.items():
if not isinstance(v, (int, float, bool, str, dict, list)) and "LuaTable" not in str(type(v)):
table[k] = None
new_dict[k] = None
continue
if "LuaTable" in str(type(v)):
d = dict(v)
if all(isinstance(i, int) for i in d.keys()):
table[k] = self._recursive_list_encode(d)
new_dict[k] = self._recursive_list_encode(d)
continue
else:
table[k] = self._recursive_dict_encode(d)
return {k: v for k, v in dict(table).items() if v is not None}
new_dict[k] = self._recursive_dict_encode(d)
return {k: v for k, v in new_dict.items() if v is not None}
def JsonEncode(self, table):
self.log.debug("requesting JsonEncode()")
@ -379,7 +441,7 @@ class Util:
# noinspection PyPep8Naming
class FP:
class FS:
def __init__(self, name: str, lua: LuaRuntime):
self.log = get_logger(f"LuaPlugin | FP | {name}")
@ -391,6 +453,8 @@ class FP:
try:
os.makedirs(path)
return True, None
except FileExistsError:
return True, None
except FileNotFoundError | NotADirectoryError as e:
return False, f"{e}"
except PermissionError as e:
@ -483,7 +547,7 @@ class FP:
item_path = os.path.join(path, item)
if os.path.isdir(item_path):
directories.append(item)
return self._lua.table_from({i: v for i, v in enumerate(directories)})
return self._lua.table_from(directories)
def ListFiles(self, path):
self.log.debug("requesting ListFiles()")
@ -492,7 +556,7 @@ class FP:
item_path = os.path.join(path, item)
if os.path.isfile(item_path):
files.append(item)
return self._lua.table_from({i: v for i, v in enumerate(files)})
return self._lua.table_from(files)
def ConcatPaths(self, *args):
self.log.debug("requesting ConcatPaths()")
@ -506,7 +570,7 @@ class LuaPluginsLoader:
self.plugins_dir = plugins_dir
self.lua_plugins = {}
self.lua_plugins_tasks = []
self.lua_dirs = []
self.lua_dirs = set()
self.log = get_logger("LuaPluginsLoader")
self.loaded_str = "Lua plugins: "
ev.register_event("_lua_plugins_get", lambda x: self.lua_plugins)
@ -514,49 +578,103 @@ class LuaPluginsLoader:
console.add_command("lua_plugins", lambda x: self.loaded_str[:-2])
console.add_command("lua_pl", lambda x: self.loaded_str[:-2])
def _start(self, obj, lua, file):
try:
f = lua.globals().loadfile(os.path.abspath(f"plugins/{obj}/{file}"))
f()
self.lua_plugins[obj]['ok'] = True
self.loaded_str += f"{obj}:ok, "
lua.globals().MP.TriggerLocalEvent("onInit")
except Exception as e:
self.loaded_str += f"{obj}:no, "
self.log.error(f"Cannot load lua plugin from `{obj}/main.lua`\n{e}")
# self.log.exception(e)
def load(self):
self.log.debug("Loading Lua plugins...")
# TODO: i18n
self.log.info("You have enabled support for Lua plugins.")
self.log.warning("There are some nuances to working with KuiToi. "
"If you have a proposal for their solution, and it is related to KuiToi, "
"please contact the developer.")
self.log.warning("Some BeamMP plugins require a correctly configured ServerConfig.toml file to function.")
self.log.info("Creating it.")
data = {
"info": "ServerConfig.toml is created solely for backward compatibility support. "
"This file will be updated every time the program is launched.",
"General": {
"Name": config.Server['name'],
"Port": config.Server['server_port'],
"AuthKey": config.Auth['key'],
"LogChat": config.Options['log_chat'],
"Debug": config.Options['debug'],
"Private": config.Auth['private'],
"MaxCars": config.Game['max_cars'],
"MaxPlayers": config.Game['players'],
"Map": f"/levels/{config.Game['map']}/info.json",
"Description": config.Server['description'],
"ResourceFolder": "plugins/"
},
"Misc": {
"ImScaredOfUpdates": False,
"SendErrorsShowMessage": False,
"SendErrors": False
},
"HTTP": {
"HTTPServerIP": config.WebAPI['server_ip'],
"HTTPServerPort": config.WebAPI['server_port'],
"SSLKeyPath": None,
"SSLCertPath": None,
"UseSSL": False,
"HTTPServerEnabled": config.WebAPI['enabled'],
}
}
with open("ServerConfig.toml", "w") as f:
toml.dump(data, f)
self.log.warning("KuiToi will not support at all: MP.Set()")
py_folders = ev.call_event("_plugins_get")[0]
for obj in os.listdir(self.plugins_dir):
path = os.path.join(self.plugins_dir, obj)
if os.path.isdir(path) and obj not in py_folders and obj not in "__pycache__":
if os.path.isfile(os.path.join(path, "main.lua")):
self.lua_dirs.append([path, obj])
for name in os.listdir(self.plugins_dir):
path = os.path.join(self.plugins_dir, name)
if os.path.isdir(path) and name not in py_folders and name not in "__pycache__":
plugin_path = os.path.join(self.plugins_dir, name)
for file in os.listdir(plugin_path):
path = f"plugins/{name}/{file}"
if os.path.isfile(path) and path.endswith(".lua"):
self.lua_dirs.add(name)
self.log.debug(f"py_folders {py_folders}, lua_dirs {self.lua_dirs}")
for path, obj in self.lua_dirs:
for name in self.lua_dirs:
# noinspection PyArgumentList
lua = LuaRuntime(encoding=config.enc, source_encoding=config.enc, unpack_returned_tuples=True)
lua.globals().printRaw = lua.globals().print
lua.globals().exit = lambda x: self.log.info(f"{obj}: You can't disable server..")
mp = MP(obj, lua)
lua.globals().MP = mp
lua.globals().print = mp._print
lua.globals().Util = Util(obj, lua)
lua.globals().FP = FP(obj, lua)
lua_globals = lua.globals()
lua_globals.printRaw = lua.globals().print
lua_globals.exit = lambda x: self.log.info(f"{name}: You can't disable server..")
mp = MP(name, lua)
lua_globals.MP = mp
lua_globals.print = mp._print
lua_globals.Util = Util(name, lua)
lua_globals.FS = FS(name, lua)
pa = os.path.abspath(self.plugins_dir)
p0 = os.path.join(pa, obj, "?.lua")
p1 = os.path.join(pa, obj, "lua", "?.lua")
lua.globals().package.path += f';{p0};{p1}'
# with open("modules/PluginsLoader/add_in.lua", "r") as f:
# code += f.read()
self.lua_plugins.update({obj: {"mp": mp, "lua": lua, "thread": None, "ok": False}})
th = Thread(target=self._start, args=(obj, lua, "main.lua"), name=f"lua_plugin_{obj}-Thread")
th.start()
self.lua_plugins[obj]['thread'] = th
p0 = os.path.join(pa, name, "?.lua")
p1 = os.path.join(pa, name, "lua", "?.lua")
lua_globals.package.path += f';{p0};{p1}'
with open("modules/PluginsLoader/add_in.lua", "r") as f:
lua.execute(f.read())
self.lua_plugins.update({name: {"lua": lua, "ok": False, "th": None, "stop_th": None}})
plugin_path = os.path.join(self.plugins_dir, name)
for file in os.listdir(plugin_path):
path = f"plugins/{name}/{file}"
if os.path.isfile(path) and path.endswith(".lua"):
try:
lua_globals.loadfile(path)()
except Exception as e:
self.loaded_str += f"{name}:no, "
self.log.error(f"Cannot load lua plugin from `{path}`: {e}")
try:
lua_globals.MP.loaded = True
lua_globals.MP._reg_ev()
lua_globals.MP.TriggerLocalEvent("onInit")
lua_globals.onInit()
self.lua_plugins[name]['ok'] = True
self.loaded_str += f"{name}:ok, "
except Exception as e:
self.loaded_str += f"{name}:no, "
self.log.error(f"Exception onInit from `{name}`: {e}")
self.log.exception(e)
def unload(self, _):
...
self.log.debug("Unloading lua plugins")
for k, data in self.lua_plugins.items():
if data['ok']:
self.log.debug(f"Unloading lua plugin: {k}")
for k, v in data['lua'].globals().MP._event_timers.items():
v.stop()

View File

@ -79,8 +79,7 @@ class PluginsLoader:
async def load(self):
self.log.debug("Loading plugins...")
files = os.listdir(self.plugins_dir)
for file in files:
for file in os.listdir(self.plugins_dir):
file_path = os.path.join(self.plugins_dir, file)
if os.path.isfile(file_path) and file.endswith(".py"):
try:
@ -96,14 +95,14 @@ class PluginsLoader:
ok = True
try:
isfunc = inspect.isfunction
if not isfunc(plugin.load):
is_func = inspect.isfunction
if not is_func(plugin.load):
self.log.error('Function "def load():" not found.')
ok = False
if not isfunc(plugin.start):
if not is_func(plugin.start):
self.log.error('Function "def start():" not found.')
ok = False
if not isfunc(plugin.unload):
if not is_func(plugin.unload):
self.log.error('Function "def unload():" not found.')
ok = False
if type(plugin.kt) != KuiToi:
@ -121,22 +120,22 @@ class PluginsLoader:
f'Plugin name: "{pl_name}"; Plugin file "{file_path}"')
plugin.open = plugin.kt.open
iscorfunc = inspect.iscoroutinefunction
is_coro_func = inspect.iscoroutinefunction
self.plugins.update(
{
pl_name: {
"plugin": plugin,
"load": {
"func": plugin.load,
"async": iscorfunc(plugin.load)
"async": is_coro_func(plugin.load)
},
"start": {
"func": plugin.start,
"async": iscorfunc(plugin.start)
"async": is_coro_func(plugin.start)
},
"unload": {
"func": plugin.unload,
"async": iscorfunc(plugin.unload)
"async": is_coro_func(plugin.unload)
}
}
}

View File

@ -11,6 +11,5 @@ from .ConfigProvider import ConfigProvider, Config
from .i18n import MultiLanguage
from .EventsSystem import EventsSystem
from .PluginsLoader import PluginsLoader
from .PluginsLoader import LuaPluginsLoader
from .WebAPISystem import web_app
from .WebAPISystem import _stop as stop_web