mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 16:25:36 +00:00
Compare commits
20 Commits
1b5ddbdd45
...
bf0a3f3feb
Author | SHA1 | Date | |
---|---|---|---|
bf0a3f3feb | |||
5e071c5705 | |||
132beb0dd6 | |||
33f2d2ba72 | |||
90113179d7 | |||
cd178b815a | |||
c1f3983856 | |||
96cc4b08db | |||
d13a319f39 | |||
f24ae23eac | |||
b31b01d137 | |||
e7be3c88be | |||
2dd8b5f5eb | |||
84c45d321a | |||
b1162af681 | |||
91c9cd8454 | |||
b8326ecdf8 | |||
c068629c83 | |||
905c0a361d | |||
d7073d9124 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -138,3 +138,5 @@ dmypy.json
|
|||||||
/test/
|
/test/
|
||||||
*test.py
|
*test.py
|
||||||
logs/
|
logs/
|
||||||
|
*.yml
|
||||||
|
*.toml
|
||||||
|
@ -19,7 +19,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
- [x] Chat
|
- [x] Chat
|
||||||
- [x] Players online counter
|
- [x] Players online counter
|
||||||
- [x] Packets handled (Recursive finding second packet)
|
- [x] Packets handled (Recursive finding second packet)
|
||||||
- [ ] Client events
|
- [x] Client events
|
||||||
- [x] Car synchronizations:
|
- [x] Car synchronizations:
|
||||||
- [x] State packets
|
- [x] State packets
|
||||||
- [x] Spawn cars
|
- [x] Spawn cars
|
||||||
@ -63,7 +63,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
- [ ] Client (Player) class
|
- [ ] Client (Player) class
|
||||||
- [x] Lua part: (Original BeamMP compatibility)
|
- [x] Lua part: (Original BeamMP compatibility)
|
||||||
- [x] Load Lua plugins
|
- [x] Load Lua plugins
|
||||||
- [x] MP Class (Excluding CreateEventTimer, CreateEventTimer)
|
- [x] MP Class
|
||||||
- [x] Util class
|
- [x] Util class
|
||||||
- [x] FS class
|
- [x] FS class
|
||||||
- [x] MultiLanguage (i18n support)
|
- [x] MultiLanguage (i18n support)
|
||||||
|
@ -7,3 +7,4 @@ starlette~=0.27.0
|
|||||||
pydantic~=2.0.2
|
pydantic~=2.0.2
|
||||||
click~=8.1.4
|
click~=8.1.4
|
||||||
lupa~=2.0
|
lupa~=2.0
|
||||||
|
toml~=0.10.2
|
@ -364,6 +364,9 @@ class Client:
|
|||||||
allow = True
|
allow = True
|
||||||
allow_snowman = True
|
allow_snowman = True
|
||||||
over_spawn = False
|
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)
|
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)
|
d2 = await ev.call_async_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
|
||||||
ev_data_list.extend(d2)
|
ev_data_list.extend(d2)
|
||||||
@ -407,6 +410,8 @@ class Client:
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
@ -444,6 +449,9 @@ class Client:
|
|||||||
|
|
||||||
allow = False
|
allow = False
|
||||||
admin_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)
|
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)
|
d2 = await ev.call_async_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
|
||||||
ev_data_list.extend(d2)
|
ev_data_list.extend(d2)
|
||||||
@ -472,8 +480,14 @@ class Client:
|
|||||||
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_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("{"):])
|
||||||
await ev.call_async_event("onCarReset", car=self.cars[car_id], car_id=car_id, player=self)
|
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}")
|
self.log.debug(f"Car reset: car_id={car_id}")
|
||||||
else:
|
else:
|
||||||
self.log.debug(f"Invalid car: car_id={car_id}")
|
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")
|
self.log.debug("Tried to send an empty event, ignoring")
|
||||||
return
|
return
|
||||||
to_ev = {"message": msg, "player": self}
|
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)
|
ev_data_list = ev.call_event("onChatReceive", **to_ev)
|
||||||
d2 = await ev.call_async_event("onChatReceive", **to_ev)
|
d2 = await ev.call_async_event("onChatReceive", **to_ev)
|
||||||
ev_data_list.extend(d2)
|
ev_data_list.extend(d2)
|
||||||
@ -555,13 +573,15 @@ class Client:
|
|||||||
if to_client:
|
if to_client:
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
writer = to_client._writer
|
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)
|
await self._send(f"C:{message}", to_all=to_all, to_self=to_self, writer=writer)
|
||||||
need_send = False
|
need_send = False
|
||||||
except KeyError | AttributeError:
|
except KeyError | AttributeError:
|
||||||
self.log.error(f"Returns invalid data: {ev_data}")
|
self.log.error(f"Returns invalid data: {ev_data}")
|
||||||
if need_send:
|
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)
|
await self._send(data, to_all=True)
|
||||||
|
|
||||||
async def _handle_codes(self, data):
|
async def _handle_codes(self, data):
|
||||||
@ -599,9 +619,11 @@ class Client:
|
|||||||
await self._send(data, to_all=True, to_self=False)
|
await self._send(data, to_all=True, to_self=False)
|
||||||
|
|
||||||
async def _looper(self):
|
async def _looper(self):
|
||||||
|
ev.call_lua_event("onPlayerConnecting", self.cid)
|
||||||
self._connect_time = time.monotonic()
|
self._connect_time = time.monotonic()
|
||||||
await self._send(f"P{self.cid}") # Send clientID
|
await self._send(f"P{self.cid}") # Send clientID
|
||||||
await self._sync_resources()
|
await self._sync_resources()
|
||||||
|
ev.call_lua_event("onPlayerJoining", self.cid)
|
||||||
tasks = self.__tasks
|
tasks = self.__tasks
|
||||||
recv = asyncio.create_task(self._recv())
|
recv = asyncio.create_task(self._recv())
|
||||||
tasks.append(recv)
|
tasks.append(recv)
|
||||||
@ -629,6 +651,10 @@ class Client:
|
|||||||
if self.ready:
|
if self.ready:
|
||||||
await self._send(f"J{self.nick} disconnected!", to_all=True, to_self=False) # I'm disconnected.
|
await self._send(f"J{self.nick} disconnected!", to_all=True, to_self=False) # I'm disconnected.
|
||||||
self.log.debug(f"Removing client")
|
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
|
# TODO: i18n
|
||||||
self.log.info(f"Disconnected, online time: {round((time.monotonic() - self._connect_time) / 60, 2)}min.")
|
self.log.info(f"Disconnected, online time: {round((time.monotonic() - self._connect_time) / 60, 2)}min.")
|
||||||
self.__Core.clients[self.cid] = None
|
self.__Core.clients[self.cid] = None
|
||||||
|
@ -11,7 +11,7 @@ __title__ = 'KuiToi-Server'
|
|||||||
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
|
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
|
||||||
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
|
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
|
||||||
__version__ = '0.4.1'
|
__version__ = '0.4.1'
|
||||||
__build__ = 1486 # Я это считаю лог файлами
|
__build__ = 1925 # Я это считаю лог файлами
|
||||||
__author__ = 'SantaSpeen'
|
__author__ = 'SantaSpeen'
|
||||||
__author_email__ = 'admin@kuitoi.su'
|
__author_email__ = 'admin@kuitoi.su'
|
||||||
__license__ = "FPA"
|
__license__ = "FPA"
|
||||||
|
@ -16,7 +16,7 @@ from core import utils
|
|||||||
from core.Client import Client
|
from core.Client import Client
|
||||||
from core.tcp_server import TCPServer
|
from core.tcp_server import TCPServer
|
||||||
from core.udp_server import UDPServer
|
from core.udp_server import UDPServer
|
||||||
from modules import PluginsLoader, LuaPluginsLoader
|
from modules import PluginsLoader
|
||||||
from modules.WebAPISystem import app as webapp
|
from modules.WebAPISystem import app as webapp
|
||||||
|
|
||||||
|
|
||||||
@ -224,8 +224,10 @@ class Core:
|
|||||||
os.mkdir(pl_dir)
|
os.mkdir(pl_dir)
|
||||||
pl = PluginsLoader(pl_dir)
|
pl = PluginsLoader(pl_dir)
|
||||||
await pl.load()
|
await pl.load()
|
||||||
lpl = LuaPluginsLoader(pl_dir)
|
if config.Options['use_lua']:
|
||||||
lpl.load()
|
from modules.PluginsLoader.lua_plugins_loader import LuaPluginsLoader
|
||||||
|
lpl = LuaPluginsLoader(pl_dir)
|
||||||
|
lpl.load()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# WebApi Start
|
# WebApi Start
|
||||||
|
@ -73,6 +73,20 @@ class TCPServer:
|
|||||||
await client.kick('Stale Client (replaced by new client)')
|
await client.kick('Stale Client (replaced by new client)')
|
||||||
return False, 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)
|
ev.call_event("onPlayerAuthenticated", player=client)
|
||||||
|
|
||||||
if len(self.Core.clients_by_id) > config.Game["players"]:
|
if len(self.Core.clients_by_id) > config.Game["players"]:
|
||||||
|
@ -10,6 +10,7 @@ import secrets
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
|
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
|
||||||
self.Auth = auth or {"key": None, "private": True}
|
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!",
|
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
|
||||||
"server_ip": "0.0.0.0", "server_port": 30814}
|
"server_ip": "0.0.0.0", "server_port": 30814}
|
||||||
self.Options = options or {"language": "en", "encoding": "utf-8", "speed_limit": 0, "use_queue": False,
|
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,
|
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
||||||
"secret_key": secrets.token_hex(16)}
|
"secret_key": secrets.token_hex(16)}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ class EventsSystem:
|
|||||||
"onCarReset": [],
|
"onCarReset": [],
|
||||||
"onSentPing": [], # Only sync
|
"onSentPing": [], # Only sync
|
||||||
"onChangePosition": [], # Only sync
|
"onChangePosition": [], # Only sync
|
||||||
|
"onPlayerDisconnect": [],
|
||||||
"onServerStopped": [],
|
"onServerStopped": [],
|
||||||
}
|
}
|
||||||
self.__async_events = {
|
self.__async_events = {
|
||||||
@ -43,6 +44,7 @@ class EventsSystem:
|
|||||||
"onCarDelete": [],
|
"onCarDelete": [],
|
||||||
"onCarEdited": [],
|
"onCarEdited": [],
|
||||||
"onCarReset": [],
|
"onCarReset": [],
|
||||||
|
"onPlayerDisconnect": [],
|
||||||
"onServerStopped": []
|
"onServerStopped": []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +73,9 @@ class EventsSystem:
|
|||||||
f"async_event={async_event}, lua_event={lua}):")
|
f"async_event={async_event}, lua_event={lua}):")
|
||||||
if lua:
|
if lua:
|
||||||
if event_name not in self.__lua_events:
|
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:
|
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")
|
self.log.debug("Register ok")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -138,12 +140,14 @@ class EventsSystem:
|
|||||||
self.log.debug(f"Calling lua event: '{event_name}'")
|
self.log.debug(f"Calling lua event: '{event_name}'")
|
||||||
funcs_data = []
|
funcs_data = []
|
||||||
if event_name in self.__lua_events.keys():
|
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:
|
try:
|
||||||
funcs_data.append(func(*args))
|
fd = func(*args)
|
||||||
|
funcs_data.append(fd)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO: i18n
|
# 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)
|
self.log.exception(e)
|
||||||
else:
|
else:
|
||||||
# TODO: i18n
|
# TODO: i18n
|
||||||
|
@ -1,2 +1 @@
|
|||||||
from .plugins_loader import PluginsLoader
|
from .plugins_loader import PluginsLoader
|
||||||
from .lua_plugins_loader import LuaPluginsLoader
|
|
||||||
|
@ -1,26 +1,100 @@
|
|||||||
package.path = package.path..";modules/PluginsLoader/lua_libs/?.lua"
|
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 = {
|
MP.CallStrategy = {
|
||||||
BestEffort = 0,
|
BestEffort = 0,
|
||||||
Precise = 1
|
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
|
||||||
|
@ -4,21 +4,53 @@ import os
|
|||||||
import platform
|
import platform
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
import toml
|
||||||
from lupa.lua53 import LuaRuntime
|
from lupa.lua53 import LuaRuntime
|
||||||
|
|
||||||
from core import get_logger
|
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
|
# noinspection PyPep8Naming
|
||||||
class MP:
|
class MP:
|
||||||
|
|
||||||
# In ./in_lua.lua
|
# In ./in_lua.lua
|
||||||
# MP.CreateTimer
|
|
||||||
# MP.Sleep
|
|
||||||
|
|
||||||
def __init__(self, name: str, lua: LuaRuntime):
|
def __init__(self, name: str, lua: LuaRuntime):
|
||||||
|
self.loaded = False
|
||||||
|
self._event_waiters = []
|
||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
self.log = get_logger(f"LuaPlugin | {name}")
|
self.log = get_logger(f"LuaPlugin | {name}")
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -29,8 +61,13 @@ class MP:
|
|||||||
"onPlayerJoin": [], "onPlayerDisconnect": [], "onChatMessage": [], "onVehicleSpawn": [],
|
"onPlayerJoin": [], "onPlayerDisconnect": [], "onChatMessage": [], "onVehicleSpawn": [],
|
||||||
"onVehicleEdited": [], "onVehicleDeleted": [], "onVehicleReset": [], "onFileChanged": []
|
"onVehicleEdited": [], "onVehicleDeleted": [], "onVehicleReset": [], "onFileChanged": []
|
||||||
}
|
}
|
||||||
|
self._event_timers = {}
|
||||||
|
|
||||||
def _print(self, *args):
|
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))
|
s = " ".join(map(str, args))
|
||||||
self.log.info(s)
|
self.log.info(s)
|
||||||
|
|
||||||
@ -45,10 +82,21 @@ class MP:
|
|||||||
self.log.debug("request MP.GetServerVersion()")
|
self.log.debug("request MP.GetServerVersion()")
|
||||||
return ev.call_event("_get_BeamMP_version")[0]
|
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:
|
def RegisterEvent(self, event_name: str, function_name: str) -> None:
|
||||||
self.log.debug("request MP.RegisterEvent()")
|
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]
|
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:
|
if event_name not in self._local_events:
|
||||||
self._local_events.update({str(event_name): [event_func]})
|
self._local_events.update({str(event_name): [event_func]})
|
||||||
else:
|
else:
|
||||||
@ -57,36 +105,45 @@ class MP:
|
|||||||
|
|
||||||
def CreateEventTimer(self, event_name: str, interval_ms: int, strategy: int = None):
|
def CreateEventTimer(self, event_name: str, interval_ms: int, strategy: int = None):
|
||||||
self.log.debug("request CreateEventTimer()")
|
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):
|
def CancelEventTimer(self, event_name: str):
|
||||||
self.log.debug("request CancelEventTimer()")
|
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):
|
def TriggerLocalEvent(self, event_name, *args):
|
||||||
self.log.debug("request TriggerLocalEvent()")
|
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 = []
|
funcs_data = []
|
||||||
if event_name in self._local_events.keys():
|
if event_name in self._local_events.keys():
|
||||||
for func in self._local_events[event_name]:
|
for func in self._local_events[event_name]:
|
||||||
try:
|
try:
|
||||||
funcs_data.append(func(*args))
|
funcs_data.append(func(*args))
|
||||||
except Exception as e:
|
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)
|
self.log.exception(e)
|
||||||
else:
|
else:
|
||||||
self.log.warning(f"Event {event_name} does not exist, maybe ev.call_lua_event() or MP.Trigger<>Event()?. "
|
self.log.warning(f"Event {event_name} does not exist, maybe ev.call_lua_event() or MP.Trigger<>Event()?. "
|
||||||
f"Just skipping it...")
|
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):
|
def TriggerGlobalEvent(self, event_name, *args):
|
||||||
self.log.debug("request TriggerGlobalEvent()")
|
self.log.debug("request TriggerGlobalEvent()")
|
||||||
return self._lua.table(
|
return self._lua.table(
|
||||||
IsDone=lambda: True,
|
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):
|
def SendChatMessage(self, player_id, message):
|
||||||
self.log.debug("request SendChatMessage()")
|
self.log.debug("request SendChatMessage()")
|
||||||
client = ev.call_event("_get_player", cid=player_id)[0]
|
client = ev.call_event("_get_player", cid=player_id)[0]
|
||||||
@ -150,6 +207,15 @@ class MP:
|
|||||||
return client.nick
|
return client.nick
|
||||||
return
|
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):
|
def RemoveVehicle(self, player_id, vehicle_id):
|
||||||
self.log.debug("request RemoveVehicle()")
|
self.log.debug("request RemoveVehicle()")
|
||||||
if player_id < 0:
|
if player_id < 0:
|
||||||
@ -165,14 +231,13 @@ 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(
|
return self._lua.table_from([f'{v["json"]}' for d in [i for i in client.cars if i is not None]
|
||||||
{i: f'{v["json"]}' for i, d in enumerate([i for i in client.cars if i is not None]) for k, v in
|
for k, v in d.items() if k == "json"])
|
||||||
d.items() if k == "json"})
|
|
||||||
|
|
||||||
def GetPlayers(self):
|
def GetPlayers(self):
|
||||||
self.log.debug("request GetPlayers()")
|
self.log.debug("request GetPlayers()")
|
||||||
clients = ev.call_event("_get_players", cid=-1)
|
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:
|
def IsPlayerGuest(self, player_id) -> bool:
|
||||||
self.log.debug("request IsPlayerGuest()")
|
self.log.debug("request IsPlayerGuest()")
|
||||||
@ -212,10 +277,6 @@ class MP:
|
|||||||
self.log.debug("request Set")
|
self.log.debug("request Set")
|
||||||
self.log.warning("KuiToi cannot support this: MP.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
|
# noinspection PyPep8Naming
|
||||||
class Util:
|
class Util:
|
||||||
@ -240,18 +301,19 @@ class Util:
|
|||||||
return [i for i in new_list if i is not None]
|
return [i for i in new_list if i is not None]
|
||||||
|
|
||||||
def _recursive_dict_encode(self, table):
|
def _recursive_dict_encode(self, table):
|
||||||
|
new_dict = dict(table)
|
||||||
for k, v in table.items():
|
for k, v in table.items():
|
||||||
if not isinstance(v, (int, float, bool, str, dict, list)) and "LuaTable" not in str(type(v)):
|
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
|
continue
|
||||||
if "LuaTable" in str(type(v)):
|
if "LuaTable" in str(type(v)):
|
||||||
d = dict(v)
|
d = dict(v)
|
||||||
if all(isinstance(i, int) for i in d.keys()):
|
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
|
continue
|
||||||
else:
|
else:
|
||||||
table[k] = self._recursive_dict_encode(d)
|
new_dict[k] = self._recursive_dict_encode(d)
|
||||||
return {k: v for k, v in dict(table).items() if v is not None}
|
return {k: v for k, v in new_dict.items() if v is not None}
|
||||||
|
|
||||||
def JsonEncode(self, table):
|
def JsonEncode(self, table):
|
||||||
self.log.debug("requesting JsonEncode()")
|
self.log.debug("requesting JsonEncode()")
|
||||||
@ -379,7 +441,7 @@ class Util:
|
|||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
class FP:
|
class FS:
|
||||||
|
|
||||||
def __init__(self, name: str, lua: LuaRuntime):
|
def __init__(self, name: str, lua: LuaRuntime):
|
||||||
self.log = get_logger(f"LuaPlugin | FP | {name}")
|
self.log = get_logger(f"LuaPlugin | FP | {name}")
|
||||||
@ -391,6 +453,8 @@ class FP:
|
|||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
return True, None
|
return True, None
|
||||||
|
except FileExistsError:
|
||||||
|
return True, None
|
||||||
except FileNotFoundError | NotADirectoryError as e:
|
except FileNotFoundError | NotADirectoryError as e:
|
||||||
return False, f"{e}"
|
return False, f"{e}"
|
||||||
except PermissionError as e:
|
except PermissionError as e:
|
||||||
@ -483,7 +547,7 @@ class FP:
|
|||||||
item_path = os.path.join(path, item)
|
item_path = os.path.join(path, item)
|
||||||
if os.path.isdir(item_path):
|
if os.path.isdir(item_path):
|
||||||
directories.append(item)
|
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):
|
def ListFiles(self, path):
|
||||||
self.log.debug("requesting ListFiles()")
|
self.log.debug("requesting ListFiles()")
|
||||||
@ -492,7 +556,7 @@ class FP:
|
|||||||
item_path = os.path.join(path, item)
|
item_path = os.path.join(path, item)
|
||||||
if os.path.isfile(item_path):
|
if os.path.isfile(item_path):
|
||||||
files.append(item)
|
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):
|
def ConcatPaths(self, *args):
|
||||||
self.log.debug("requesting ConcatPaths()")
|
self.log.debug("requesting ConcatPaths()")
|
||||||
@ -506,7 +570,7 @@ class LuaPluginsLoader:
|
|||||||
self.plugins_dir = plugins_dir
|
self.plugins_dir = plugins_dir
|
||||||
self.lua_plugins = {}
|
self.lua_plugins = {}
|
||||||
self.lua_plugins_tasks = []
|
self.lua_plugins_tasks = []
|
||||||
self.lua_dirs = []
|
self.lua_dirs = set()
|
||||||
self.log = get_logger("LuaPluginsLoader")
|
self.log = get_logger("LuaPluginsLoader")
|
||||||
self.loaded_str = "Lua plugins: "
|
self.loaded_str = "Lua plugins: "
|
||||||
ev.register_event("_lua_plugins_get", lambda x: self.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_plugins", lambda x: self.loaded_str[:-2])
|
||||||
console.add_command("lua_pl", 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):
|
def load(self):
|
||||||
self.log.debug("Loading Lua plugins...")
|
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]
|
py_folders = ev.call_event("_plugins_get")[0]
|
||||||
for obj in os.listdir(self.plugins_dir):
|
for name in os.listdir(self.plugins_dir):
|
||||||
path = os.path.join(self.plugins_dir, obj)
|
path = os.path.join(self.plugins_dir, name)
|
||||||
if os.path.isdir(path) and obj not in py_folders and obj not in "__pycache__":
|
if os.path.isdir(path) and name not in py_folders and name not in "__pycache__":
|
||||||
if os.path.isfile(os.path.join(path, "main.lua")):
|
plugin_path = os.path.join(self.plugins_dir, name)
|
||||||
self.lua_dirs.append([path, obj])
|
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}")
|
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
|
# noinspection PyArgumentList
|
||||||
lua = LuaRuntime(encoding=config.enc, source_encoding=config.enc, unpack_returned_tuples=True)
|
lua = LuaRuntime(encoding=config.enc, source_encoding=config.enc, unpack_returned_tuples=True)
|
||||||
lua.globals().printRaw = lua.globals().print
|
lua_globals = lua.globals()
|
||||||
lua.globals().exit = lambda x: self.log.info(f"{obj}: You can't disable server..")
|
lua_globals.printRaw = lua.globals().print
|
||||||
mp = MP(obj, lua)
|
lua_globals.exit = lambda x: self.log.info(f"{name}: You can't disable server..")
|
||||||
lua.globals().MP = mp
|
mp = MP(name, lua)
|
||||||
lua.globals().print = mp._print
|
lua_globals.MP = mp
|
||||||
lua.globals().Util = Util(obj, lua)
|
lua_globals.print = mp._print
|
||||||
lua.globals().FP = FP(obj, lua)
|
lua_globals.Util = Util(name, lua)
|
||||||
|
lua_globals.FS = FS(name, lua)
|
||||||
pa = os.path.abspath(self.plugins_dir)
|
pa = os.path.abspath(self.plugins_dir)
|
||||||
p0 = os.path.join(pa, obj, "?.lua")
|
p0 = os.path.join(pa, name, "?.lua")
|
||||||
p1 = os.path.join(pa, obj, "lua", "?.lua")
|
p1 = os.path.join(pa, name, "lua", "?.lua")
|
||||||
lua.globals().package.path += f';{p0};{p1}'
|
lua_globals.package.path += f';{p0};{p1}'
|
||||||
# with open("modules/PluginsLoader/add_in.lua", "r") as f:
|
with open("modules/PluginsLoader/add_in.lua", "r") as f:
|
||||||
# code += f.read()
|
lua.execute(f.read())
|
||||||
self.lua_plugins.update({obj: {"mp": mp, "lua": lua, "thread": None, "ok": False}})
|
self.lua_plugins.update({name: {"lua": lua, "ok": False, "th": None, "stop_th": None}})
|
||||||
th = Thread(target=self._start, args=(obj, lua, "main.lua"), name=f"lua_plugin_{obj}-Thread")
|
plugin_path = os.path.join(self.plugins_dir, name)
|
||||||
th.start()
|
for file in os.listdir(plugin_path):
|
||||||
self.lua_plugins[obj]['thread'] = th
|
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, _):
|
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()
|
||||||
|
@ -79,8 +79,7 @@ class PluginsLoader:
|
|||||||
|
|
||||||
async def load(self):
|
async def load(self):
|
||||||
self.log.debug("Loading plugins...")
|
self.log.debug("Loading plugins...")
|
||||||
files = os.listdir(self.plugins_dir)
|
for file in os.listdir(self.plugins_dir):
|
||||||
for file in files:
|
|
||||||
file_path = os.path.join(self.plugins_dir, file)
|
file_path = os.path.join(self.plugins_dir, file)
|
||||||
if os.path.isfile(file_path) and file.endswith(".py"):
|
if os.path.isfile(file_path) and file.endswith(".py"):
|
||||||
try:
|
try:
|
||||||
@ -96,14 +95,14 @@ class PluginsLoader:
|
|||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
try:
|
try:
|
||||||
isfunc = inspect.isfunction
|
is_func = inspect.isfunction
|
||||||
if not isfunc(plugin.load):
|
if not is_func(plugin.load):
|
||||||
self.log.error('Function "def load():" not found.')
|
self.log.error('Function "def load():" not found.')
|
||||||
ok = False
|
ok = False
|
||||||
if not isfunc(plugin.start):
|
if not is_func(plugin.start):
|
||||||
self.log.error('Function "def start():" not found.')
|
self.log.error('Function "def start():" not found.')
|
||||||
ok = False
|
ok = False
|
||||||
if not isfunc(plugin.unload):
|
if not is_func(plugin.unload):
|
||||||
self.log.error('Function "def unload():" not found.')
|
self.log.error('Function "def unload():" not found.')
|
||||||
ok = False
|
ok = False
|
||||||
if type(plugin.kt) != KuiToi:
|
if type(plugin.kt) != KuiToi:
|
||||||
@ -121,22 +120,22 @@ class PluginsLoader:
|
|||||||
f'Plugin name: "{pl_name}"; Plugin file "{file_path}"')
|
f'Plugin name: "{pl_name}"; Plugin file "{file_path}"')
|
||||||
|
|
||||||
plugin.open = plugin.kt.open
|
plugin.open = plugin.kt.open
|
||||||
iscorfunc = inspect.iscoroutinefunction
|
is_coro_func = inspect.iscoroutinefunction
|
||||||
self.plugins.update(
|
self.plugins.update(
|
||||||
{
|
{
|
||||||
pl_name: {
|
pl_name: {
|
||||||
"plugin": plugin,
|
"plugin": plugin,
|
||||||
"load": {
|
"load": {
|
||||||
"func": plugin.load,
|
"func": plugin.load,
|
||||||
"async": iscorfunc(plugin.load)
|
"async": is_coro_func(plugin.load)
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"func": plugin.start,
|
"func": plugin.start,
|
||||||
"async": iscorfunc(plugin.start)
|
"async": is_coro_func(plugin.start)
|
||||||
},
|
},
|
||||||
"unload": {
|
"unload": {
|
||||||
"func": plugin.unload,
|
"func": plugin.unload,
|
||||||
"async": iscorfunc(plugin.unload)
|
"async": is_coro_func(plugin.unload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,5 @@ from .ConfigProvider import ConfigProvider, Config
|
|||||||
from .i18n import MultiLanguage
|
from .i18n import MultiLanguage
|
||||||
from .EventsSystem import EventsSystem
|
from .EventsSystem import EventsSystem
|
||||||
from .PluginsLoader import PluginsLoader
|
from .PluginsLoader import PluginsLoader
|
||||||
from .PluginsLoader import LuaPluginsLoader
|
|
||||||
from .WebAPISystem import web_app
|
from .WebAPISystem import web_app
|
||||||
from .WebAPISystem import _stop as stop_web
|
from .WebAPISystem import _stop as stop_web
|
||||||
|
Loading…
x
Reference in New Issue
Block a user