Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ssl_cert =
ssl_key =

[OPN]
Enabled = ON
IP = 0.0.0.0
ExternalIP =
Port = 8200
Expand All @@ -27,6 +28,7 @@ LogToFile = ON
LogToWindow = ON

[LMP]
Enabled = ON
IP = 0.0.0.0
ExternalIP =
Port = 8201
Expand All @@ -41,6 +43,7 @@ LogToFile = ON
LogToWindow = ON

[FMP]
Enabled = ON
IP = 0.0.0.0
ExternalIP =
Port = 8202
Expand All @@ -55,6 +58,7 @@ LogToFile = ON
LogToWindow = ON

[RFP]
Enabled = ON
IP = 0.0.0.0
ExternalIP =
Port = 8203
Expand Down
13 changes: 12 additions & 1 deletion fmp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,29 @@
import mh.pat_item as pati
from mh.constants import PatID4
from mh.pat import PatRequestHandler, PatServer
from mh.session import FMPSession
from mh.state import State
from other.utils import hexdump, server_base, server_main, to_str


class FmpServer(PatServer):
"""Basic FMP server class."""
# TODO: Backport close cache logic
pass
def __init__(self, *args, **kwargs):
PatServer.__init__(self, *args, **kwargs)
self.fmp_state = State()
# TODO: Backport the cache server registration logic instead
import mh.database as db
db.get_instance().servers = self.fmp_state.servers


class FmpRequestHandler(PatRequestHandler):
"""Basic FMP server request handler class."""

def setup(self):
super(FmpRequestHandler, self).setup()
self.session = FMPSession(self.session)

def recvAnsConnection(self, packet_id, data, seq):
"""AnsConnection packet."""
connection_data = pati.ConnectionData.unpack(data)
Expand Down
68 changes: 45 additions & 23 deletions master_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,73 +14,96 @@
import rfp_server as RFP

from other.debug import register_debug_signal, dry_run
from other.python import TYPE_CHECKING
from other.utils import create_server_from_base

if TYPE_CHECKING:
from argparse import Namespace # noqa: F401
from collections.abc import Sequence # noqa: F401
from typing import Any # noqa: F401

from mh.server import BasicPatServer # noqa: F401


def create_servers(server_args):
# type: (Sequence[str]) -> tuple[list[BasicPatServer], bool]
"""Create servers and check if it has ui."""
servers = []
servers = [] # type: list[BasicPatServer]
has_ui = False
for module in (OPN, LMP, FMP, RFP):
server, args = create_server_from_base(*module.BASE, args=server_args)
has_ui = has_ui or args.log_to_window
servers.append(server)
server, args = create_server_from_base(*module.BASE,
cmd_args=server_args) # type: ignore[misc] # noqa: E501
if server and args:
has_ui = has_ui or args.log_to_window
servers.append(server)
return servers, has_ui


def main(args):
# type: (Namespace) -> None
"""Master server main function."""
register_debug_signal()

servers, has_ui = create_servers(server_args=args.args)
threads = [
threading.Thread(
threads_map = {
server: threading.Thread(
target=server.serve_forever,
name="{}.serve_forever".format(server.__class__.__name__)
)
for server in servers
]
}
# TODO: Backport cache's logic (i.e. new thread, maintain_connection)
for thread in threads:
thread.start()

def interactive_mode(local=locals()):
# type: (dict[str, Any]) -> None
"""Run an interactive python interpreter in another thread."""
import code
code.interact(local=local)

repl_thread = threading.Thread(target=interactive_mode)

if has_ui:
from other.ui import update as ui_update
ui_update()
else:
def ui_update():
# type: () -> None
"""No-op."""
pass

# noinspection PyBroadException
try:
ui_update()
for thread in threads_map.values():
thread.start()

if args.interactive:
t = threading.Thread(target=interactive_mode)
t.start()
repl_thread.start()

if args.dry_run:
dry_run()

while threads:
for thread in threads:
if has_ui:
ui_update()
while threads_map:
for server, thread in threads_map.items():
ui_update()
if not thread.is_alive():
threads.remove(thread)
server.error("Server thread died: %s", thread.name)
del threads_map[server]
# TODO: Add restart option?
break
thread.join(0.1)

if args.interactive:
t.join()
except KeyboardInterrupt:
print("Interrupt key was pressed, closing server...")
print("Interrupt key was pressed, closing servers...")
except Exception:
print('Unexpected exception caught...')
traceback.print_exc()
sys.exit(1)
finally:
for server in servers:
server.close()
server.shutdown()
server.server_close()
if args.interactive and repl_thread.is_alive():
repl_thread.join()


if __name__ == "__main__":
Expand All @@ -97,5 +120,4 @@ def interactive_mode(local=locals()):
# - no_timeout is currently available as a server argument as follows:
parser.add_argument("args", nargs='*',
help="arguments forwarded to all servers")
args = parser.parse_args()
main(args)
main(parser.parse_args())
2 changes: 1 addition & 1 deletion mh/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def make_binary_trading_post():
FMP_VERSION = 1
# TODO: Backport central and NATNEG constants

TIME_STATE = 0
TIME_STATE = 0 # FIXME: Unused since quest rotation added, see time_utils
IS_JAP = False


Expand Down
Loading