Skip to content

Commit bdc8071

Browse files
committed
nicer
1 parent be1db96 commit bdc8071

File tree

11 files changed

+104
-79
lines changed

11 files changed

+104
-79
lines changed

chatbridge/common/logger.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import os
22
import sys
33
import time
4+
import weakref
45
import zipfile
56
from logging import FileHandler, Formatter, Logger, DEBUG, StreamHandler, INFO
67
from threading import RLock
7-
from typing import Optional
8+
from typing import Optional, Set
89

910
from colorlog import ColoredFormatter
1011

1112
LOGGING_DIR = os.path.join('logs')
12-
DEBUG_SWITCH = False
1313

1414

1515
class SyncStdoutStreamHandler(StreamHandler):
@@ -63,6 +63,14 @@ class ChatBridgeLogger(Logger):
6363
'CRITICAL': 'red'
6464
}
6565
}
66+
__DEBUG_SWITCH = False
67+
__REFS: Set['ChatBridgeLogger'] = weakref.WeakSet()
68+
69+
@classmethod
70+
def set_debug_all(cls, value: bool):
71+
cls.__DEBUG_SWITCH = value
72+
for logger in cls.__REFS:
73+
logger.__refresh_debug_level()
6674

6775
def __init__(self, name: str, *, file_name: Optional[str] = None, file_handler: Optional[FileHandler] = None):
6876
super().__init__(name)
@@ -80,7 +88,15 @@ def __init__(self, name: str, *, file_name: Optional[str] = None, file_handler:
8088
self.file_handler = file_handler
8189
if self.file_handler is not None:
8290
self.addHandler(self.file_handler)
83-
self.setLevel(DEBUG if DEBUG_SWITCH else INFO)
91+
self.__REFS.add(self)
92+
self.__refresh_debug_level()
93+
94+
@classmethod
95+
def is_debug_enabled(cls) -> bool:
96+
return cls.__DEBUG_SWITCH
97+
98+
def __refresh_debug_level(self):
99+
self.setLevel(DEBUG if self.__DEBUG_SWITCH else INFO)
84100

85101
def close_file(self):
86102
if self.file_handler is not None:

chatbridge/core/client.py

+28-35
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121

2222
class ClientStatus(Enum):
23+
STARTING = auto() # thread started
2324
CONNECTING = auto() # socket connecting
2425
CONNECTED = auto() # socket connected, logging in
2526
ONLINE = auto() # logged in, thread started
@@ -68,7 +69,7 @@ def _in_status(self, status: Union[ClientStatus, Collection[ClientStatus]]):
6869

6970
def _assert_status(self, status: Union[ClientStatus, Collection[ClientStatus]]):
7071
if not self._in_status(status):
71-
raise AssertionError('Excepted status {} but {} found'.format(set(status), self.__status))
72+
raise AssertionError('Excepted status {} but {} found'.format(status, self.__status))
7273

7374
def _is_connected(self) -> bool:
7475
return self._in_status({ClientStatus.CONNECTED, ClientStatus.ONLINE})
@@ -113,7 +114,7 @@ def __connect(self):
113114
"""
114115
status: STOPPED -> CONNECTED
115116
"""
116-
self._assert_status(ClientStatus.STOPPED)
117+
self._assert_status(ClientStatus.STARTING)
117118
self._set_status(ClientStatus.CONNECTING)
118119
assert self.__server_address is not None
119120
self.logger.info('Connecting to {}'.format(self.__server_address))
@@ -156,37 +157,26 @@ def _tick_connection(self):
156157
# --------------
157158

158159
def start(self):
159-
assert self._on_external_thread()
160-
if not self._is_stopped():
161-
self.logger.warning('Client is running, cannot start again')
162-
return
163-
acq = self.__start_stop_lock.acquire(blocking=False)
164-
if not acq:
165-
self.logger.warning('Client is already starting')
166-
return
167-
try:
168-
self.logger.debug('Starting client')
169-
self.__connection_done.clear()
170-
super().start()
171-
self.__connection_done.wait()
172-
finally:
173-
self.__start_stop_lock.release()
160+
self.logger.debug('Starting client')
161+
with self.__start_stop_lock:
162+
if not self._is_stopped():
163+
self.logger.warning('Client is running, cannot start again')
164+
return
165+
self._set_status(ClientStatus.STARTING)
166+
self.__connection_done.clear()
167+
super().start()
168+
self.__connection_done.wait()
169+
self.logger.debug('Started client')
174170

175171
def stop(self):
176-
assert self._on_external_thread()
177-
if self._is_stopped():
178-
self.logger.warning('Client is stopped, cannot stop again')
179-
return
180-
acq = self.__start_stop_lock.acquire(blocking=False)
181-
if not acq:
182-
self.logger.warning('Client is already stopping')
183-
return
184-
try:
185-
self.logger.debug('Stopping client')
186-
self.__disconnect()
187-
super().stop()
188-
finally:
189-
self.__start_stop_lock.release()
172+
self.logger.debug('Stopping client')
173+
with self.__start_stop_lock:
174+
if self._is_stopped():
175+
self.logger.warning('Client is stopped, cannot stop again')
176+
return
177+
self.__disconnect() # state -> STOPPED or DISCONNECTED
178+
super().stop()
179+
self.logger.debug('Stopped client')
190180

191181
def restart(self):
192182
self.logger.info('Restarting client')
@@ -205,7 +195,7 @@ def _main_loop(self):
205195
self._connect_and_login()
206196
self._set_status(ClientStatus.ONLINE)
207197
except Exception as e:
208-
self.logger.error('Failed to connect to {}: {}'.format(self.__server_address, e))
198+
(self.logger.exception if self.logger.is_debug_enabled() else self.logger.error)('Failed to connect to {}: {}'.format(self.__server_address, e))
209199
self.__disconnect()
210200
self.__connection_done.set()
211201
else:
@@ -230,11 +220,14 @@ def _main_loop(self):
230220

231221
def _on_started(self):
232222
self.__connection_done.set()
233-
self._start_keep_alive_thread()
223+
self.__thread_keep_alive = self._start_keep_alive_thread()
234224

235225
def _on_stopped(self):
236226
if self._is_connected():
237227
self.__disconnect()
228+
self.logger.debug('Joining keep alive thread')
229+
self.__thread_keep_alive.join()
230+
self.logger.debug('Joined keep alive thread')
238231

239232
# ----------------
240233
# Packet Logic
@@ -323,8 +316,8 @@ def reply_command(self, target: str, asker_payload: 'CommandPayload', result: Un
323316
def _get_keep_alive_thread_name(cls):
324317
return 'KeepAlive'
325318

326-
def _start_keep_alive_thread(self):
327-
self._start_thread(self._keep_alive_loop, self._get_keep_alive_thread_name())
319+
def _start_keep_alive_thread(self) -> Thread:
320+
return self._start_thread(self._keep_alive_loop, self._get_keep_alive_thread_name())
328321

329322
def _keep_alive_target(self) -> str:
330323
return constants.SERVER_NAME

chatbridge/core/network/basic.py

+15-25
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ def __init__(self, name: str, aes_key: str):
2121
self.aes_key = aes_key
2222
self._cryptor = AESCryptor(aes_key)
2323
self.__thread_run: Optional[Thread] = None
24-
self.__threads: List[Thread] = []
25-
self.__threads_lock = RLock()
24+
self.__thread_run_lock = RLock()
2625

2726
def get_name(self) -> str:
2827
return self.__name
@@ -39,7 +38,7 @@ def get_logging_file_name(self) -> Optional[str]:
3938
def _start_thread(self, target: Callable, name: str) -> Thread:
4039
thread = Thread(target=target, args=(), name=name, daemon=True)
4140
thread.start()
42-
self.__threads.append(thread)
41+
self.logger.debug('Started thread {}: {}'.format(name, thread))
4342
return thread
4443

4544
@classmethod
@@ -49,9 +48,11 @@ def _get_main_loop_thread_name(cls):
4948
def start(self):
5049
def func():
5150
self._main_loop()
52-
self.__clean_up()
51+
self.logger.close_file()
52+
with self.__thread_run_lock:
53+
self.__thread_run = None
5354

54-
with self.__threads_lock:
55+
with self.__thread_run_lock:
5556
if self.__thread_run is not None:
5657
raise RuntimeError('Already running')
5758
self.__thread_run = self._start_thread(func, self._get_main_loop_thread_name())
@@ -61,26 +62,15 @@ def stop(self):
6162
Stop the client/server, and wait until the MainLoop thread exits
6263
Need to be called on a non-MainLoop thread
6364
"""
64-
self.__join_threads()
65+
self.logger.debug('Joining MainLoop thread')
66+
with self.__thread_run_lock:
67+
thread = self.__thread_run
68+
if thread is not None:
69+
if thread is not current_thread():
70+
thread.join()
71+
else:
72+
self.logger.warning('Joining current thread {}'.format(thread))
73+
self.logger.debug('Joined MainLoop thread')
6574

6675
def _main_loop(self):
6776
pass
68-
69-
def __clean_up(self):
70-
self.logger.close_file()
71-
self.__thread_run = None
72-
73-
def __join_threads(self):
74-
self.logger.debug('Joining threads {}'.format(self.__threads))
75-
with self.__threads_lock:
76-
for thread in self.__threads:
77-
if thread is not current_thread():
78-
thread.join()
79-
else:
80-
self.logger.warning('Joining current thread {}'.format(thread))
81-
self.__threads.clear()
82-
self.__thread_run = None
83-
self.logger.debug('Joined threads')
84-
85-
def _on_external_thread(self):
86-
return current_thread() not in self.__threads

chatbridge/core/server.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,13 @@ def start(self):
123123

124124
def __stop(self):
125125
self.__stopping_flag = True
126-
try:
127-
self.__sock.close()
128-
except:
129-
pass
126+
if self.__sock is not None:
127+
try:
128+
self.__sock.close()
129+
self.__sock = None
130+
self.logger.info('Socket closed')
131+
except:
132+
self.logger.exception('Error when stop close')
130133

131134
def stop(self):
132135
self.__stop()

chatbridge/impl/cli/cli_client.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def _on_started(self):
1212
self.logger.info('Connected to the server')
1313

1414
def _on_stopped(self):
15-
super()._on_started()
15+
super()._on_stopped()
1616
self.logger.info('Disconnected')
1717

1818
def on_chat(self, sender: str, payload: ChatPayload):
@@ -43,6 +43,7 @@ def console_loop(self):
4343
def main():
4444
config: ClientConfig = utils.load_config(ConfigFile, ClientConfig)
4545
client = CLIClient.create(config)
46+
client.logger.set_debug_all(True)
4647
print('AES Key = {}'.format(config.aes_key))
4748
print('Client Info: name = {}, password = {}'.format(config.name, config.password))
4849
print('Server address = {}'.format(client.get_server_address()))

chatbridge/impl/cli/cli_server.py

+7
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,17 @@ def console_loop(self):
2929
self.logger.info('Client count: {}'.format(len(self.clients)))
3030
for client in self.clients.values():
3131
self.logger.info('- {}: online = {}, ping = {}'.format(client.info.name, client.is_online(), client.get_ping_text()))
32+
elif text == 'debug on':
33+
self.logger.set_debug_all(True)
34+
self.logger.info('Debug logging on')
35+
elif text == 'debug off':
36+
self.logger.set_debug_all(False)
37+
self.logger.info('Debug logging off')
3238
else:
3339
self.logger.info('stop": stop the server')
3440
self.logger.info('stop <client_name>": stop a client')
3541
self.logger.info('list": show the client list')
42+
self.logger.info('debug on|off": switch debug logging')
3643

3744

3845
def main():

chatbridge/impl/mcdr/client.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ def _on_stopped(self):
4141
self.logger.info('Client stopped')
4242

4343
def on_chat(self, sender: str, payload: ChatPayload):
44-
if self.server is not None:
45-
self.server.say(RText('[{}] {}'.format(sender, payload.formatted_str()), RColor.gray))
44+
self.server.say(RText('[{}] {}'.format(sender, payload.formatted_str()), RColor.gray))
4645

4746
def on_command(self, sender: str, payload: CommandPayload):
4847
command = payload.command

chatbridge/impl/mcdr/mcdr_entry.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import shutil
23
from threading import Event, Lock
34
from typing import Optional
45

@@ -29,7 +30,7 @@ def display_status(source: CommandSource):
2930
if config is None or client is None:
3031
source.reply(tr('status.not_init'))
3132
else:
32-
source.reply(tr('status.not_init', client.is_online()))
33+
source.reply(tr('status.info', client.is_online(), client.get_ping_text()))
3334

3435

3536
@new_thread('ChatBridge-restart')
@@ -43,6 +44,7 @@ def restart_client(source: CommandSource):
4344
def on_unload(server: PluginServerInterface):
4445
with cb_lock:
4546
if client is not None and client.is_online():
47+
server.logger.info('Stopping chatbridge client due to plugin unload')
4648
client.stop()
4749
cb_stop_done.set()
4850

@@ -57,21 +59,28 @@ def send_chat(message: str, *, author: str = ''):
5759

5860

5961
def on_load(server: PluginServerInterface, old_module):
62+
cb1_config_path = os.path.join('config', 'ChatBridge_client.json')
63+
config_path = os.path.join(server.get_data_folder(), 'config.json')
64+
if os.path.isfile(cb1_config_path) and not os.path.isfile(config_path):
65+
shutil.copyfile(cb1_config_path, config_path)
66+
server.logger.info('Migrated configure file from ChatBridge v1: {} -> {}'.format(cb1_config_path, config_path))
67+
server.logger.info('You need to delete the old config file manually if you want')
68+
6069
global client, config
61-
if not os.path.isfile(os.path.join('config', server.get_self_metadata().id, 'config.json')):
70+
if not os.path.isfile(config_path):
6271
server.logger.exception('Config file not found! ChatBridge will not work properly')
6372
server.logger.error('Fill the default configure file with correct values and reload the plugin')
6473
server.save_config_simple(MCDRClientConfig.get_default())
6574
return
6675

6776
try:
68-
config = server.load_config_simple(target_class=MCDRClientConfig)
77+
config = server.load_config_simple(file_name=config_path, in_data_folder=False, target_class=MCDRClientConfig)
6978
except:
7079
server.logger.exception('Failed to read the config file! ChatBridge might not work properly')
7180
server.logger.error('Fix the configure file and then reload the plugin')
72-
if config.debug:
73-
logger.DEBUG_SWITCH = True
7481
client = ChatBridgeMCDRClient(config, server)
82+
if config.debug:
83+
client.logger.set_debug_all(True)
7584
for prefix in Prefixes:
7685
server.register_help_message(prefix, tr('help_summary'))
7786
server.register_command(
@@ -88,6 +97,7 @@ def start():
8897
stop_event: Event = old_module.cb_stop_done
8998
if not stop_event.wait(30):
9099
server.logger.warning('Previous chatbridge instance does not stop for 30s')
100+
server.logger.info('Starting chatbridge client')
91101
client.start()
92102

93103
start()

lang/en_us.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ chatbridge:
99
help_summary: 'Chatbridge control'
1010
status:
1111
not_init: 'Chatbridge is not initialized, please check the server console output'
12-
info: 'Chatbridge online state: {}'
13-
restarted: 'Chatbridge restarted'
12+
info: |
13+
Chatbridge status:
14+
Online: {0}
15+
ping: {1}
16+
restarted: 'Chatbridge restarted'

lang/zh_cn.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ chatbridge:
99
help_summary: '跨服聊天控制'
1010
status:
1111
not_init: '跨服聊天未初始化,请检查服务端后台输出'
12-
info: '跨服聊天客户端在线情况: {}'
12+
info: |
13+
跨服聊天状态:
14+
在线: {0}
15+
延迟: {1}
1316
restarted: '跨服聊天已重启'

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
mcdreforged>=2.0
1+
mcdreforged>=2.1.2
22
pycryptodome
33
colorlog

0 commit comments

Comments
 (0)