mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2026-04-24 00:56:36 +00:00
Compare commits
12 Commits
0.4.4-beta
...
cb6adde7c2
| Author | SHA1 | Date | |
|---|---|---|---|
| cb6adde7c2 | |||
| 422dd35a8f | |||
| 2c2bd1cb4a | |||
| 7eba3d5877 | |||
| abbd64184e | |||
| e5dd63579b | |||
| ef286b7e03 | |||
| 3a42fa13e7 | |||
| cdec0b9949 | |||
| de91d075b4 | |||
| a7c02e0b52 | |||
| 6dd3de63a9 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -140,3 +140,6 @@ dmypy.json
|
||||
logs/
|
||||
*.yml
|
||||
*.toml
|
||||
|
||||
/win-ver_info.txt
|
||||
/output/
|
||||
@@ -75,14 +75,15 @@ I didn't like writing plugins in Lua after using Python; it was very inconvenien
|
||||
- [ ] Sync with event system
|
||||
- [ ] Add methods...
|
||||
- [ ] RCON System:
|
||||
- [ ] Serving
|
||||
- [ ] Client
|
||||
- [x] Serving
|
||||
- [ ] Handle commands
|
||||
- [x] Client
|
||||
- [x] AES encryption
|
||||
- [ ] KuiToi System
|
||||
- [ ] Servers counter
|
||||
- [ ] Players counter
|
||||
- [ ] Etc.
|
||||
- [ ] [Documentation](./docs/)
|
||||
- [ ] [Documentation](./docs)
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -44,6 +44,10 @@ class Client:
|
||||
def _writer(self):
|
||||
return self.__writer
|
||||
|
||||
@property
|
||||
def alive(self):
|
||||
return self.__alive
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
return self._log
|
||||
@@ -185,8 +189,8 @@ class Client:
|
||||
await writer.drain()
|
||||
return True
|
||||
|
||||
except ConnectionError:
|
||||
self.log.debug('[TCP] Disconnected')
|
||||
except Exception as e:
|
||||
self.log.debug(f'[TCP] Disconnected: {e}')
|
||||
self.__alive = False
|
||||
await self._remove_me()
|
||||
return False
|
||||
@@ -194,7 +198,13 @@ class Client:
|
||||
async def _recv(self, one=False):
|
||||
while self.__alive:
|
||||
try:
|
||||
header = await self.__reader.read(4)
|
||||
header = b""
|
||||
while len(header) < 4 and self.__alive:
|
||||
h = await self.__reader.read(4)
|
||||
if not h:
|
||||
break
|
||||
else:
|
||||
header += h
|
||||
|
||||
int_header = int.from_bytes(header, byteorder='little', signed=True)
|
||||
|
||||
@@ -219,9 +229,14 @@ class Client:
|
||||
self.__packets_queue.append(None)
|
||||
continue
|
||||
|
||||
data = await self.__reader.read(int_header)
|
||||
data = b""
|
||||
while len(data) < int_header and self.__alive:
|
||||
buffer = await self.__reader.read(int_header - len(data))
|
||||
if not buffer:
|
||||
break
|
||||
else:
|
||||
data += buffer
|
||||
|
||||
# self.log.debug(f"int_header: {int_header}; data: `{data}`;")
|
||||
abg = b"ABG:"
|
||||
if len(data) > len(abg) and data.startswith(abg):
|
||||
data = zlib.decompress(data[len(abg):])
|
||||
@@ -250,7 +265,7 @@ class Client:
|
||||
try:
|
||||
writer.write(data)
|
||||
await writer.drain()
|
||||
self.log.debug(f"[{who}] Sent {len(data)} bytes.")
|
||||
# self.log.debug(f"[{who}] Sent {len(data)} bytes.")
|
||||
except ConnectionError:
|
||||
self.__alive = False
|
||||
self.log.debug(f"[{who}] Disconnected.")
|
||||
@@ -269,6 +284,9 @@ class Client:
|
||||
async def _sync_resources(self):
|
||||
while self.__alive:
|
||||
data = await self._recv(True)
|
||||
if data is None:
|
||||
await self._remove_me()
|
||||
break
|
||||
if data.startswith(b"f"):
|
||||
file = data[1:].decode(config.enc)
|
||||
self.log.info(i18n.client_mod_request.format(repr(file)))
|
||||
@@ -307,7 +325,7 @@ class Client:
|
||||
self._split_load(half_size, size, True, file, speed)
|
||||
]
|
||||
sl0, sl1 = await asyncio.gather(*uploads)
|
||||
tr = time.monotonic() - t
|
||||
tr = (time.monotonic() - t) or 0.0001
|
||||
if self.__Core.lock_upload:
|
||||
self.__Core.lock_upload = False
|
||||
msg = i18n.client_mod_sent.format(round(size / MB, 3), math.ceil(size / tr / MB), int(tr))
|
||||
@@ -347,7 +365,7 @@ class Client:
|
||||
id_sep = s.find('-')
|
||||
if id_sep == -1:
|
||||
self.log.debug(
|
||||
f"Invalid packet: Could not parse pid/vid from packet, as there is no '-' separator: '{data}'")
|
||||
f"Invalid packet: Could not parse pid/vid from packet, as there is no '-' separator: '{data}', {s}")
|
||||
return -1, -1
|
||||
cid = s[:id_sep]
|
||||
vid = s[id_sep + 1:]
|
||||
@@ -497,6 +515,12 @@ class Client:
|
||||
else:
|
||||
self.log.debug(f"Invalid car: car_id={car_id}")
|
||||
|
||||
async def reset_car(self, car_id, x, y, z, rot=None):
|
||||
# TODO: reset_car
|
||||
self.log.debug(f"Resetting car from plugin")
|
||||
if rot is None:
|
||||
rot = {"y": 0, "w": 0, "x": 0, "z": 0}
|
||||
|
||||
async def _reset_car(self, raw_data):
|
||||
cid, car_id = self._get_cid_vid(raw_data)
|
||||
if car_id != -1 and cid == self.cid and self._cars[car_id]:
|
||||
@@ -538,7 +562,7 @@ class Client:
|
||||
|
||||
case "t": # Broken details
|
||||
self.log.debug(f"Something changed/broken: {raw_data}")
|
||||
cid, car_id = self._get_cid_vid(raw_data[5:])
|
||||
cid, car_id = self._get_cid_vid(raw_data)
|
||||
if car_id != -1 and cid == self.cid and self._cars[car_id]:
|
||||
data = raw_data[raw_data.find("{"):]
|
||||
ev.call_event("onCarChanged", car_id=car_id, data=data)
|
||||
@@ -547,7 +571,7 @@ class Client:
|
||||
|
||||
case "m": # Move focus car
|
||||
self.log.debug(f"Move focus to: {raw_data}")
|
||||
cid, car_id = self._get_cid_vid(raw_data[5:])
|
||||
cid, car_id = self._get_cid_vid(raw_data[3:])
|
||||
if car_id != -1 and cid == self.cid and self._cars[car_id]:
|
||||
self._focus_car = car_id
|
||||
data = raw_data[raw_data.find("{"):]
|
||||
|
||||
@@ -42,6 +42,8 @@ class Client:
|
||||
@property
|
||||
def _writer(self) -> StreamWriter: ...
|
||||
@property
|
||||
def alive(self) -> bool: ...
|
||||
@property
|
||||
def log(self) -> Logger: ...
|
||||
@property
|
||||
def addr(self) -> Tuple[str, int]: ...
|
||||
|
||||
@@ -30,7 +30,7 @@ from modules import ConfigProvider, EventsSystem
|
||||
from modules import Console
|
||||
from modules import MultiLanguage
|
||||
|
||||
args = parser.parse_args()
|
||||
args, _ = parser.parse_known_args()
|
||||
if args.version:
|
||||
print(f"{__title__}:\n\tVersion: {__version__}\n\tBuild: {__build__}")
|
||||
exit(0)
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
# Licence: FPA
|
||||
# (c) kuitoi.su 2023
|
||||
import asyncio
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import aiohttp
|
||||
import uvicorn
|
||||
|
||||
from core import utils
|
||||
from core import utils, __version__
|
||||
from core.Client import Client
|
||||
from core.tcp_server import TCPServer
|
||||
from core.udp_server import UDPServer
|
||||
@@ -26,6 +28,7 @@ class Core:
|
||||
def __init__(self):
|
||||
self.log = utils.get_logger("core")
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.start_time = time.monotonic()
|
||||
self.run = False
|
||||
self.direct = False
|
||||
self.clients = []
|
||||
@@ -111,6 +114,8 @@ class Core:
|
||||
if not client.ready:
|
||||
client.is_disconnected()
|
||||
continue
|
||||
if not client.alive:
|
||||
await client.kick("You are not alive!")
|
||||
await client._send(ca)
|
||||
except Exception as e:
|
||||
self.log.error("Error in check_alive.")
|
||||
@@ -217,6 +222,22 @@ class Core:
|
||||
except Exception as e:
|
||||
self.log.error(f"Error in heartbeat: {e}")
|
||||
|
||||
async def kick_cmd(self, args):
|
||||
if not len(args) > 0:
|
||||
return "\nUsage: kick <nick>|:<id> [reason]\nExamples:\n\tkick admin bad boy\n\tkick :0 bad boy"
|
||||
reason = "kicked by console."
|
||||
if len(args) > 1:
|
||||
reason = " ".join(args[1:])
|
||||
cl = args[0]
|
||||
if cl.startswith(":") and cl[1:].isdigit():
|
||||
client = self.get_client(cid=int(cl[1:]))
|
||||
else:
|
||||
client = self.get_client(nick=cl)
|
||||
if client:
|
||||
await client.kick(reason)
|
||||
else:
|
||||
return "Client not found."
|
||||
|
||||
async def main(self):
|
||||
self.tcp = self.tcp(self, self.server_ip, self.server_port)
|
||||
self.udp = self.udp(self, self.server_ip, self.server_port)
|
||||
@@ -224,6 +245,7 @@ class Core:
|
||||
"list",
|
||||
lambda x: f"Players list: {self.get_clients_list(True)}"
|
||||
)
|
||||
console.add_command("kick", self.kick_cmd)
|
||||
|
||||
pl_dir = "plugins"
|
||||
self.log.debug("Initializing PluginsLoaders...")
|
||||
@@ -271,6 +293,7 @@ class Core:
|
||||
# self.udp.start,
|
||||
f_tasks = [self.tcp.start, self.udp._start, console.start, self.stop_me, self.heartbeat, self.check_alive]
|
||||
if config.RCON['enabled']:
|
||||
console.rcon.version = f"KuiToi {__version__}"
|
||||
rcon = console.rcon(config.RCON['password'], config.RCON['server_ip'], config.RCON['server_port'])
|
||||
f_tasks.append(rcon.start)
|
||||
for task in f_tasks:
|
||||
@@ -307,6 +330,12 @@ class Core:
|
||||
ev.call_event("_lua_plugins_unload")
|
||||
await ev.call_async_event("_plugins_unload")
|
||||
self.run = False
|
||||
self.log.info(i18n.stop)
|
||||
if config.WebAPI["enabled"]:
|
||||
asyncio.run(self.web_stop())
|
||||
total_time = time.monotonic() - self.start_time
|
||||
hours = int(total_time // 3600)
|
||||
minutes = int((total_time % 3600) // 60)
|
||||
seconds = math.ceil(total_time % 60)
|
||||
t = f"{'' if not hours else f'{hours} hours, '}{'' if not hours else f'{minutes} min., '}{seconds} sec."
|
||||
self.log.info(f"Working time: {t}")
|
||||
self.log.info(i18n.stop)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# Licence: FPA
|
||||
# (c) kuitoi.su 2023
|
||||
import asyncio
|
||||
import time
|
||||
from threading import Thread
|
||||
from typing import Callable, List, Dict
|
||||
|
||||
@@ -16,6 +17,7 @@ from .udp_server import UDPServer
|
||||
|
||||
class Core:
|
||||
def __init__(self):
|
||||
self.start_time = time.monotonic()
|
||||
self.log = utils.get_logger("core")
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.run = False
|
||||
@@ -45,6 +47,7 @@ class Core:
|
||||
def start_web() -> None: ...
|
||||
def stop_me(self) -> None: ...
|
||||
async def heartbeat(self, test=False) -> None: ...
|
||||
async def kick_cmd(self, args: list) -> None | str: ...
|
||||
async def main(self) -> None: ...
|
||||
def start(self) -> None: ...
|
||||
async def stop(self) -> None: ...
|
||||
|
||||
@@ -100,8 +100,8 @@ class UDPServer(asyncio.DatagramTransport):
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
except OSError as e:
|
||||
self.run = False
|
||||
self.Core.run = False
|
||||
# self.run = False
|
||||
# self.Core.run = False
|
||||
self.log.error(f"Cannot bind port or other error: {e}")
|
||||
except Exception as e:
|
||||
self.log.error(f"Error: {e}")
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import asyncio
|
||||
import binascii
|
||||
import hashlib
|
||||
import os
|
||||
import zlib
|
||||
from base64 import b64decode, b64encode
|
||||
|
||||
from cryptography.hazmat.primitives import padding
|
||||
@@ -7,86 +10,177 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
from core import get_logger
|
||||
|
||||
"""
|
||||
shared key: SHA256 of "password"
|
||||
<header>: "\x00\x00\x00\x00" (Byte order: Little Endian) - like you use
|
||||
<iv>: A set of random bytes packed in base64 (New for each message)
|
||||
-> To server
|
||||
<- From server
|
||||
|
||||
Open TCP connection /
|
||||
| -> "<iv>:hello" Without header, immediately with AES encryption (shared key)
|
||||
| *Decrypt and some processes*
|
||||
| Fail /
|
||||
| | <- ":E:Bad key" | ":E:Error Message" Without header, without AES encryption
|
||||
| | tcp.close() # End
|
||||
| Success /
|
||||
| | <- "<iv>:hello" with header, with AES encryption
|
||||
| | (Next, everywhere with header, with AES encryption)
|
||||
| -> "<iv>:<header>Cs:ver"
|
||||
| <- "<iv>:<header>Os:KuiToi 0.4.3 | "<iv>:<header>Os:BeamMP 3.2.0"
|
||||
| # Prints server and they version
|
||||
| -> "<iv>:<header>Cs:commands"
|
||||
| <- "<iv>:<header>Os:stop,help,plugins" | "<iv>:<header>Os:SKIP" For an autocomplete; "SKIP" For no autocomplete;
|
||||
| *Ready to handle commands*
|
||||
| -> "<iv>:<header>C:help"
|
||||
| <- "<iv>:<header>O:stop: very cool stop\nhelp: Yayayayoy"
|
||||
| -> "<iv>:<header>C:...."
|
||||
| <- "<iv>:<header>O:...."
|
||||
| -> "<iv>:<header>C:exit"
|
||||
| tcp.close()
|
||||
|
||||
Codes:
|
||||
* "hello" - Hello message
|
||||
* "E:error_message" - Send RCON error
|
||||
* "C:command" - Receive command
|
||||
* "Cs:" - Receive system command
|
||||
* "O:output" - Send command output
|
||||
* "Os:" - Send system output
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class RCONSystem:
|
||||
console = None
|
||||
version = "verError"
|
||||
|
||||
def __init__(self, key, host, port):
|
||||
self.log = get_logger("RCON")
|
||||
self.key = key
|
||||
self.key = hashlib.sha256(key.encode(config.enc)).digest()
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.run = False
|
||||
|
||||
def encrypt(self, message, key):
|
||||
def _encrypt(self, message):
|
||||
self.log.debug(f"Encrypt message: {message}")
|
||||
key = hashlib.sha256(key).digest()
|
||||
iv = os.urandom(16)
|
||||
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||
cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv))
|
||||
encryptor = cipher.encryptor()
|
||||
padder = padding.PKCS7(algorithms.AES.block_size).padder()
|
||||
padded_data = padder.update(message.encode('utf-8')) + padder.finalize()
|
||||
padded_data = padder.update(message) + padder.finalize()
|
||||
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
|
||||
encoded_data = b64encode(encrypted_data)
|
||||
encoded_data = b64encode(zlib.compress(encrypted_data, level=zlib.Z_BEST_COMPRESSION))
|
||||
encoded_iv = b64encode(iv)
|
||||
return encoded_iv + b":" + encoded_data
|
||||
|
||||
def decrypt(self, ciphertext, key):
|
||||
self.log.debug(f"Dencrypt message: {ciphertext}")
|
||||
key = hashlib.sha256(key).digest()
|
||||
encoded_iv, encoded_data = ciphertext.split(":")
|
||||
def _decrypt(self, ciphertext):
|
||||
self.log.debug(f"Decrypt message: {ciphertext}")
|
||||
encoded_iv, encoded_data = ciphertext.split(b":", 2)
|
||||
iv = b64decode(encoded_iv)
|
||||
encrypted_data = b64decode(encoded_data)
|
||||
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||
encrypted_data = zlib.decompress(b64decode(encoded_data))
|
||||
cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv))
|
||||
decryptor = cipher.decryptor()
|
||||
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
||||
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
|
||||
return unpadded_data.decode('utf-8')
|
||||
return unpadded_data
|
||||
|
||||
async def handle_client(self):
|
||||
pass
|
||||
async def _recv(self, reader, writer) -> tuple[str, bool]:
|
||||
try:
|
||||
header = b""
|
||||
while len(header) < 4:
|
||||
h = await reader.read(4 - len(header))
|
||||
if not h:
|
||||
break
|
||||
else:
|
||||
header += h
|
||||
header = int.from_bytes(header, byteorder='little', signed=True)
|
||||
if header <= 0:
|
||||
self.log.warning("Connection closed!")
|
||||
writer.close()
|
||||
|
||||
encrypted_data = b""
|
||||
while len(encrypted_data) < header:
|
||||
buffer = await reader.read(header - len(encrypted_data))
|
||||
if not buffer:
|
||||
break
|
||||
else:
|
||||
encrypted_data += buffer
|
||||
try:
|
||||
data, s = self._decrypt(encrypted_data), True
|
||||
except binascii.Error:
|
||||
data, s = encrypted_data, False
|
||||
except ValueError:
|
||||
data, s = encrypted_data, False
|
||||
|
||||
self.log.debug(f"Received: {data}, {s}")
|
||||
return data.decode(config.enc), s
|
||||
except ConnectionResetError:
|
||||
self.log.warning("Connection reset.")
|
||||
return "", False
|
||||
|
||||
async def _send(self, data, writer, encrypt=True, warn=True):
|
||||
self.log.debug(f"Sending: \"{data}\"")
|
||||
if isinstance(data, str):
|
||||
data = data.encode(config.enc)
|
||||
|
||||
if encrypt:
|
||||
data = self._encrypt(data)
|
||||
self.log.debug(f"Send encrypted: {data}")
|
||||
|
||||
header = len(data).to_bytes(4, "little", signed=True)
|
||||
try:
|
||||
writer.write(header + data)
|
||||
await writer.drain()
|
||||
return True
|
||||
except ConnectionError:
|
||||
self.log.debug("Sending error...")
|
||||
if encrypt and warn:
|
||||
self.log.warning("Connection closed!")
|
||||
return False
|
||||
|
||||
async def send_hello(self, writer, work):
|
||||
while work[0]:
|
||||
await asyncio.sleep(5)
|
||||
if not await self._send("Cs:hello", writer, warn=False):
|
||||
work[0] = False
|
||||
writer.close()
|
||||
break
|
||||
|
||||
async def while_handle(self, reader, writer):
|
||||
ver, status = await self._recv(reader, writer)
|
||||
if ver == "ver" and status:
|
||||
await self._send(self.version, writer)
|
||||
cmds, status = await self._recv(reader, writer)
|
||||
if cmds == "commands" and status:
|
||||
await self._send("SKIP", writer)
|
||||
work = [True]
|
||||
t = asyncio.create_task(self.send_hello(writer, work))
|
||||
while work[0]:
|
||||
data, status = await self._recv(reader, writer)
|
||||
if not status:
|
||||
work[0] = False
|
||||
writer.close()
|
||||
break
|
||||
code = data[:2]
|
||||
message = data[data.find(":") + 1:]
|
||||
match code:
|
||||
case "Cs":
|
||||
match message:
|
||||
case "hello":
|
||||
await self._send("Os:hello", writer)
|
||||
case _:
|
||||
self.log.warning(f"Unknown command: {data}")
|
||||
case "C:":
|
||||
self.log.info(f"Called the command: {message}")
|
||||
if message == "exit":
|
||||
self.log.info("Connection closed.")
|
||||
writer.close()
|
||||
work[0] = False
|
||||
break
|
||||
|
||||
case "Os":
|
||||
match message:
|
||||
case "hello":
|
||||
pass
|
||||
# await self._send("Cs:hello", writer)
|
||||
case _:
|
||||
self.log.warning(f"Unknown command: {data}")
|
||||
case "O:":
|
||||
pass
|
||||
case _:
|
||||
self.log.warning(f"Unknown command: {data}")
|
||||
|
||||
await t
|
||||
|
||||
async def handle_connect(self, reader, writer):
|
||||
try:
|
||||
hello, status = await self._recv(reader, writer)
|
||||
if hello == "hello" and status:
|
||||
await self._send("hello", writer)
|
||||
await self.while_handle(reader, writer)
|
||||
else:
|
||||
await self._send("E:Wrong password", writer, False)
|
||||
writer.close()
|
||||
except Exception as e:
|
||||
self.log.error("Error while handling connection...")
|
||||
self.log.exception(e)
|
||||
|
||||
async def start(self):
|
||||
self.log.info("TODO: RCON")
|
||||
|
||||
async def stop(self):
|
||||
pass
|
||||
self.run = True
|
||||
try:
|
||||
server = await asyncio.start_server(self.handle_connect, self.host, self.port, backlog=5)
|
||||
self.log.info(f"RCON server started on {server.sockets[0].getsockname()!r}")
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
except OSError as e:
|
||||
self.log.error(i18n.core_bind_failed.format(e))
|
||||
raise e
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.log.error(f"Error: {e}")
|
||||
raise e
|
||||
finally:
|
||||
self.run = False
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# Licence: FPA
|
||||
# (c) kuitoi.su 2023
|
||||
import builtins
|
||||
import inspect
|
||||
import logging
|
||||
from typing import AnyStr
|
||||
|
||||
@@ -18,7 +19,7 @@ from prompt_toolkit.output.win32 import NoConsoleScreenBufferError
|
||||
from prompt_toolkit.patch_stdout import patch_stdout
|
||||
|
||||
from core import get_logger
|
||||
from modules.ConsoleSystem import RCON
|
||||
from modules.ConsoleSystem.RCON import RCONSystem
|
||||
|
||||
|
||||
class Console:
|
||||
@@ -46,7 +47,7 @@ class Console:
|
||||
self.add_command("help", self.__create_help_message, i18n.man_message_help, i18n.help_message_help,
|
||||
custom_completer={"help": {"--raw": None}})
|
||||
self.completer = NestedCompleter.from_nested_dict(self.__alias)
|
||||
rcon = RCON
|
||||
rcon = RCONSystem
|
||||
rcon.console = self
|
||||
self.rcon = rcon
|
||||
|
||||
@@ -242,7 +243,11 @@ class Console:
|
||||
self.log(text)
|
||||
command_object = self.__func.get(cmd)
|
||||
if command_object:
|
||||
out = command_object['f'](cmd_s[1:])
|
||||
func = command_object['f']
|
||||
if inspect.iscoroutinefunction(func):
|
||||
out = await func(cmd_s[1:])
|
||||
else:
|
||||
out = func(cmd_s[1:])
|
||||
if out:
|
||||
self.log(out)
|
||||
else:
|
||||
|
||||
@@ -73,6 +73,11 @@ class EventsSystem:
|
||||
self.log.debug("used builtins_hook")
|
||||
builtins.ev = self
|
||||
|
||||
def is_event(self, event_name):
|
||||
return (event_name in self.__async_events.keys() or
|
||||
event_name in self.__events.keys() or
|
||||
event_name in self.__lua_events.keys())
|
||||
|
||||
def register_event(self, event_name, event_func, async_event=False, lua=None):
|
||||
self.log.debug(f"register_event(event_name='{event_name}', event_func='{event_func}', "
|
||||
f"async_event={async_event}, lua_event={lua}):")
|
||||
@@ -112,7 +117,7 @@ class EventsSystem:
|
||||
except Exception as e:
|
||||
self.log.error(i18n.events_calling_error.format(event_name, func.__name__))
|
||||
self.log.exception(e)
|
||||
else:
|
||||
elif not self.is_event(event_name):
|
||||
self.log.warning(i18n.events_not_found.format(event_name, "kt.call_event()"))
|
||||
|
||||
return funcs_data
|
||||
@@ -130,7 +135,7 @@ class EventsSystem:
|
||||
except Exception as e:
|
||||
self.log.error(i18n.events_calling_error.format(event_name, func.__name__))
|
||||
self.log.exception(e)
|
||||
else:
|
||||
elif not self.is_event(event_name):
|
||||
self.log.warning(i18n.events_not_found.format(event_name, "kt.call_async_event()"))
|
||||
|
||||
return funcs_data
|
||||
@@ -151,7 +156,7 @@ class EventsSystem:
|
||||
funcs_data.append(fd)
|
||||
except Exception as e:
|
||||
self.log.error(i18n.events_lua_calling_error.format(f"{e}", event_name, func_name, f"{args}"))
|
||||
else:
|
||||
elif not self.is_event(event_name):
|
||||
self.log.warning(i18n.events_not_found.format(event_name, "ev.call_lua_event(), MP.Trigger<>Event()"))
|
||||
|
||||
return funcs_data
|
||||
|
||||
BIN
win-logo.ico
Normal file
BIN
win-logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
19
win-metadata.yml
Normal file
19
win-metadata.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# pip install pyinstaller-versionfile
|
||||
# create-version-file win-metadata.yml --outfile win-ver_info.txt
|
||||
Version: 0.4.3
|
||||
CompanyName: KuiToi
|
||||
FileDescription: KuiToi Server
|
||||
InternalName: KuiToi Server
|
||||
LegalCopyright: © Maxim Khomutov
|
||||
OriginalFilename: KuiToi-Server.exe
|
||||
ProductName: KuiToi Server
|
||||
Translation:
|
||||
# ru-RU
|
||||
- langID: 1049
|
||||
charsetID: 1251
|
||||
# en-US
|
||||
- langID: 1033
|
||||
charsetID: 1251
|
||||
# zh-CN
|
||||
- langID: 2052
|
||||
charsetID: 950
|
||||
85
win-pyinstaller.json
Normal file
85
win-pyinstaller.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"version": "auto-py-to-exe-configuration_v1",
|
||||
"pyinstallerOptions": [
|
||||
{
|
||||
"optionDest": "noconfirm",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "filenames",
|
||||
"value": "C:/Users/Santa/PycharmProjects/kuitoi-Server/src/main.py"
|
||||
},
|
||||
{
|
||||
"optionDest": "onefile",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "console",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "icon_file",
|
||||
"value": "C:/Users/Santa/PycharmProjects/kuitoi-Server/win-logo.ico"
|
||||
},
|
||||
{
|
||||
"optionDest": "name",
|
||||
"value": "KuiToi-Server"
|
||||
},
|
||||
{
|
||||
"optionDest": "ascii",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "clean_build",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "strip",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "noupx",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "disable_windowed_traceback",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "version_file",
|
||||
"value": "C:/Users/Santa/PycharmProjects/kuitoi-Server/win-ver_info.txt"
|
||||
},
|
||||
{
|
||||
"optionDest": "embed_manifest",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_admin",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_uiaccess",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "win_private_assemblies",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "win_no_prefer_redirects",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "bootloader_ignore_signals",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "argv_emulation",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"nonPyinstallerOptions": {
|
||||
"increaseRecursionLimit": true,
|
||||
"manualArguments": ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user