Skip to content

Commit

Permalink
v1.7.3 HSL服务器功能菜单 phase 1
Browse files Browse the repository at this point in the history
  • Loading branch information
Hikari committed Oct 22, 2024
1 parent 4fc30a1 commit cd3d91a
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 98 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ jobs:
upload_url: ${{ needs.create_release.outputs.upload_url }}
asset_path: Linux Build/HikariServerLauncher-${{ needs.tag_version.outputs.new_tag }}.bin
asset_name: HikariServerLauncher-${{ needs.tag_version.outputs.new_tag }}.bin
- name: ls
run: ls -R


- name: Upload Release Asset(Windows)
if: ${{ matrix.os == 'windows-latest' }}
id: upload_release_asset_win
Expand Down
2 changes: 1 addition & 1 deletion hsl.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": 17,
"minor": 2
"minor": 3
}
59 changes: 5 additions & 54 deletions hsl/core/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
# DOWNLOAD_SOURCE = r'https://hsl.hikari.bond/source.json'
# SPCONFIGS_SOURCE = r'https://hsl.hikari.bond/spconfigs.json'
VERSION_SOURCE = r'https://hsl.hikari.bond/hsl.json'
SEND_COUNTER = r'http://a.hikari.bond:40654/count'
HSL_VERSION = 17
HSL_VERSION_MINOR = 2
HSL_VERSION_MINOR = 3

console = Console()
async def make_request(url: str, error_message: str) -> dict:
Expand All @@ -28,59 +29,9 @@ async def check_update():
console.print(f'[bold magenta]发现主要版本更新,版本号:[u]{latest/10}[/u],建议及时更新')
return
if (HSL_VERSION_MINOR < minor and HSL_VERSION == latest):
console.print(f'[bold magenta]发现次要版本更新,版本号:[u]{minor/10}[/u],建议及时更新')
console.print(f'[bold magenta]发现次要版本更新,版本号:[u]{latest/10}.{minor}[/u],建议及时更新')
return
def get_version() -> tuple:
return HSL_VERSION, HSL_VERSION_MINOR
# async def get_cache_update() -> bool:
# data = await make_request(VERSION_SOURCE, error_message='检查更新失败')
# remote_spconfig = data.get('spconfig', 0)
# remote_source = data.get('source', 0)
# if os.path.exists(SPCONFIG_CACHE_FILE):
# with open(SPCONFIG_CACHE_FILE, 'r', encoding='utf-8') as f:
# cached_spconfig_version = json.load(f).get('version', 0)
# console.print(f'Cached SPConfig version: {cached_spconfig_version}')
# else:
# cached_spconfig_version = 0
# if os.path.exists(SOURCE_CACHE_FILE):
# with open(SOURCE_CACHE_FILE, 'r', encoding='utf-8') as f:
# cached_source_version = json.load(f)[0].get('version', 0)
# console.print(f'Cached Source version: {cached_source_version}')
# else:
# cached_source_version = 0
# if (
# remote_spconfig > cached_spconfig_version or
# remote_source > cached_source_version
# ):
# console.print('Cache outdated, updating...')
# return await update_cache()
# return True

# async def update_cache() -> bool:
# """Update cache
# """
# spconfig_data = await make_request(SPCONFIGS_SOURCE, error_message='更新特定配置缓存失败')
# with open(SPCONFIG_CACHE_FILE, 'w', encoding='utf-8') as f:
# json.dump(spconfig_data, f, ensure_ascii=False, indent=4)
# source_data = await make_request(DOWNLOAD_SOURCE, error_message='更新下载源缓存失败')
# with open(SOURCE_CACHE_FILE, 'w', encoding='utf-8') as f:
# json.dump(source_data, f, ensure_ascii=False, indent=4)
# return bool((spconfig_data and source_data))
# async def load_source() -> Source:
# """Load source data from sources
# """
# if os.path.exists(SOURCE_CACHE_FILE):
# with open(SOURCE_CACHE_FILE, 'r', encoding='utf-8') as f:
# return Source(**json.load(f))
# await get_cache_update()
# return await load_source()
# # _source = make_request(DOWNLOAD_SOURCE, error_message='加载源数据失败')
# # return Source(**_source)
# async def get_spconfigs() -> dict:
# if os.path.exists(SPCONFIG_CACHE_FILE):
# with open(SPCONFIG_CACHE_FILE, 'r', encoding='utf-8') as f:
# return json.load(f)
# await get_cache_update()
# return await get_spconfigs()
# async def get_version_data() -> tuple:
# return HSL_VERSION, HSL_VERSION_MINOR, *await check_update()
async def send_counter():
await make_request(SEND_COUNTER, error_message='发送计数失败')
1 change: 1 addition & 0 deletions hsl/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def __init__(self):
self.autorun: str = ''
self.debug: bool = False
self.language: str = 'zh'
self.shell_introduction_read: bool = False
def load(self):
try:
with open(CONFIG_FILE, 'r') as f:
Expand Down
113 changes: 84 additions & 29 deletions hsl/core/server.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import os
from xml.dom.pulldom import START_DOCUMENT
from typing import Dict, Callable
import regex
import signal
import psutil
import asyncio
import subprocess
from hsl.core.java import Java
from hsl.core.main import HSL
from hsl.utils.prompt import promptSelect
from queue import Queue
from threading import Thread
from aioconsole import ainput
from rich.console import Console
from rich.style import Style
START_SERVER = regex.compile(r'.*Starting minecraft server version.*')
START_PORT = regex.compile(r'.*Starting Minecraft server on.*')
PREPARE_LEVEL = regex.compile(r'.*Preparing level ".*"')
Expand All @@ -26,6 +29,7 @@
OFFLINE_SERVER: 'server-offline-mode-enabled',
STOP_SERVER:'server-stopping'
}
FLAG_OUTPUT_ENABLE = True
console = Console(style='dim')
output_counter = 0
class Server(HSL):
Expand All @@ -42,7 +46,11 @@ def __init__(self, *, name: str, type: str, path: str, javaversion: str, maxRam:
self.javaversion = javaversion
self.maxRam = maxRam
self.data = data

self.constants_init()
def constants_init(self):
global HSL_FUNCTION_MENU, PROCESS_FUNCTIONS
HSL_FUNCTION_MENU = self.locale.trans_key(['process-functions', 'cancel'])
PROCESS_FUNCTIONS = self.locale.trans_key(['process-functions-kill','process-functions-sigstop','process-functions-sigcont', 'cancel'])
def pathJoin(self, path: str) -> str:
return os.path.join(os.getcwd(), self.path, path)
async def analysis_output(self, output_text: str):
Expand All @@ -51,15 +59,16 @@ async def analysis_output(self, output_text: str):
console.print(self.locale.trans_key('hsl-assist-log-analyzer-text-prefix') + self.locale.trans_key('hsl-assist-log-analyzer-text-server-log-found'))
output_counter += 1
for key, value in ASSIST_LOG_ANALYSIS_KEY.items():
if key.match(output_text):
if key.match(output_text) and FLAG_OUTPUT_ENABLE:
console.print(self.locale.trans_key('hsl-assist-log-analyzer-text-prefix') + self.locale.trans_key(f'hsl-assist-log-analyzer-text-{value}'))
return
async def Output(self, process):
async def Output(self, process: subprocess.Popen):
global FLAG_OUTPUT_ENABLE
linetext = ''
processed_lines = set()
console.print('[bold magenta][HSL][yellow]开始启动服务器...')
while True:
for line in iter(process.stdout.readline, b''):
for line in iter(process.stdout.readline, b''): # type: ignore
try:
linetext = line.decode('utf-8').strip()
except UnicodeDecodeError:
Expand All @@ -68,41 +77,78 @@ async def Output(self, process):
continue
if linetext not in processed_lines:
processed_lines.add(linetext)

console.print(linetext)
if FLAG_OUTPUT_ENABLE:
console.print(linetext,markup=False,style=Style(bgcolor='#2c2c2c',color='white'))
await self.analysis_output(linetext)
if process.poll() is not None:
break
return
def consoleOutput(self, process):
def consoleOutput(self, process: subprocess.Popen):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(self.Output(process))
async def get_input(self, process, input_queue: Queue):
async def process_write_input(self, process: subprocess.Popen, input: str):
process.stdin.write(input.encode('utf-8') + b'\n') # type: ignore
process.stdin.flush() # type: ignore
async def get_input(self, process: subprocess.Popen, input_queue: Queue):
while True:
try:
command_input = await ainput(f'{self.name} >>> ')
except (KeyboardInterrupt,asyncio.exceptions.CancelledError):
console.log("已退出输入")
process.kill()
break
except EOFError:
command_input = await ainput(f'({self.name}) >>> ')
except (KeyboardInterrupt, EOFError):
console.log("已退出输入")
process.kill()
break
except Exception as e:
console.log(f'输入错误: {e}')
continue
if command_input.strip().lower() == 'hsl':
global FLAG_OUTPUT_ENABLE
FLAG_OUTPUT_ENABLE = False
await self.hsl_function_menu(process)
FLAG_OUTPUT_ENABLE = True
continue
input_queue.put(command_input)
if input_queue.get() is None:
break
try:
process.stdin.write(command_input.encode('utf-8') + b'\n')
process.stdin.flush()
await self.process_write_input(process, command_input)
except OSError:
pass
return
def consoleInput(self, process, input_queue: Queue):
async def hsl_function_menu(self, process: subprocess.Popen):
_index = await promptSelect(HSL_FUNCTION_MENU, self.locale.trans_key('hsl-shell-functions-text'))
hsl_functions:Dict[int,Callable] = {
0: lambda: self.process_functions(process),
len(HSL_FUNCTION_MENU) - 1: lambda: asyncio.sleep(0)
}
await hsl_functions[_index]()
async def process_functions(self,process: subprocess.Popen):
_index = await promptSelect(PROCESS_FUNCTIONS, self.locale.trans_key('process-functions'))
process_functions:Dict[int,Callable] = {
0: lambda: self.process_kill(process),
1: lambda: self.process_sigstop(process),
2: lambda: self.process_sigcont(process),
len(PROCESS_FUNCTIONS) - 1: lambda: asyncio.sleep(0)
}
await process_functions[_index]()
async def process_kill(self, process: subprocess.Popen):
process.kill()
console.print(self.locale.trans_key('process-functions-kill-success'))
async def process_sigstop(self, process: subprocess.Popen):
if os.name == 'nt':
_process = psutil.Process(process.pid)
_process.suspend()
else:
os.kill(process.pid, signal.SIGSTOP)
console.print(self.locale.trans_key('process-functions-sigstop-success'))
async def process_sigcont(self, process: subprocess.Popen):
if os.name == 'nt':
_process = psutil.Process(process.pid)
_process.resume()
else:
os.kill(process.pid, signal.SIGCONT)
console.print(self.locale.trans_key('process-functions-sigcont-success'))
def consoleInput(self, process: subprocess.Popen, input_queue: Queue):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(self.get_input(process, input_queue))
Expand Down Expand Up @@ -174,6 +220,11 @@ def _build_run_command(self, javaexecPath, export=False):
if export else f"forge-{mcVersion}-{forgeVersion}.jar"
])
async def run(self, path: str):
if not self.config.shell_introduction_read:
console.print(self.locale.trans_key('hsl-shell-introduction'))
await promptSelect(self.locale.trans_key(['yes']), self.locale.trans_key('hsl-shell-introduction-prompt-select'))
self.config.shell_introduction_read = True
self.config.save()
startup_cmd = self.data.get('startup_cmd', '')
if not startup_cmd:
console.log('[bold red]启动命令为空')
Expand All @@ -182,11 +233,13 @@ async def run(self, path: str):
subprocess.Popen(startup_cmd, cwd=self.path)
except Exception as e:
console.log(f'[bold red]启动命令执行失败: {e}')
# if 'startup_cmd' in self.data:
# if not self.data.get('startup_cmd', ''):
# console.log('[bold red]启动命令为空')
# else:
# subprocess.Popen(self.data['startup_cmd'], cwd=self.path)
startup_cmd = self.data.get('startup_cmd', '')
if not startup_cmd:
console.log('[bold red]启动前执行命令为空')
else:
os.system(startup_cmd)
console.log(self.locale.trans_key('command-execute-before-server-ran'))
await asyncio.sleep(1)

run_command = await self.gen_run_command(path)

Expand All @@ -203,7 +256,6 @@ async def run(self, path: str):
stderr=subprocess.STDOUT,
)
input_queue = Queue()

#t1 = Thread(target=self.consoleOutput, args=(table, process, output_queue))
t1 = Thread(target=self.consoleOutput, args=(process,))
t2 = Thread(target=self.consoleInput, args=(process, input_queue))
Expand All @@ -212,9 +264,12 @@ async def run(self, path: str):
t1.start()
t2.start()


t1.join()
console.print('[bold green]键入Ctrl+C以退出控制台')
t2.join()
console.print('[bold green]控制台已退出')
try:
t1.join()
console.print('[bold green]键入Ctrl+C以退出控制台')
t2.join()
console.print('[bold green]控制台已退出')
except KeyboardInterrupt:
console.print('[bold red]已退出控制台')
process.kill()
return
6 changes: 3 additions & 3 deletions hsl/utils/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from hsl.core.locale import Locale
import asyncio
locale = Locale()
OPTIONS_YN = locale.trans_key(['yes','no'])
async def promptSelect(options: list,prompt: str) -> int:
OPTIONS_YN: list['str'] = locale.trans_key(['yes','no'])
async def promptSelect(options: list['str'],prompt: str) -> int:
choices = [Choice(options[i],data=i) for i in range(len(options))]
prompt_task = asyncio.create_task(ListPrompt(prompt, question_mark='[HSL]', pointer='>', choices=choices,annotation=locale.trans_key('choice-prompt-annotation')).prompt_async())
select = await asyncio.gather(prompt_task)
Expand All @@ -18,7 +18,7 @@ async def promptConfirm(prompt: str) -> bool:
# confirm = await asyncio.gather(prompt_task)
# return confirm[0]
return bool(await promptSelect(OPTIONS_YN,prompt) == 0)
async def promptSelectRed(options: list,prompt: str) -> int:
async def promptSelectRed(options: list[str],prompt: str) -> int:
choices = [Choice(options[i],data=i) for i in range(len(options))]
prompt_task = asyncio.create_task(ListPrompt(prompt, question_mark='[???]', pointer='>', choices=choices,annotation=locale.trans_key('choice-prompt-annotation')).prompt_async(style=Style([("unselected", "#ff0000"), ("selected", "#ff0000")])))
select = await asyncio.gather(prompt_task)
Expand Down
18 changes: 16 additions & 2 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"open-server-folder": "Open The Server Folder",
"specific-configs": "Specific Configuration",
"command-execute-before-server-start": "Commands Executed Before The Server Start",
"command-execute-before-server-start-prompt-input": "Please enter the command that will be executed in the server directory before the server starts.",
"command-execute-before-server-start-prompt-input": "Please enter the command that will be executed in the HSL directory before the server starts.",
"command-execute-before-server-ran": "[bold green]Command executed successfully.",
"custom-jvm-args": "Customize JVM parameters",
"set-to-auto-run": "Set To Auto-run",
"set-to-auto-run-ask": "Are you sure you want to set {servername} to auto-run?",
Expand Down Expand Up @@ -135,5 +136,18 @@
"jokes-4": "HSL is not very useful, do you want to switch to another tool?",
"jokes-5": "HSL has not been updated for a long time, do you want to retire?",
"jokes-6": "HSL has detected 10000 bugs, do you want to go to the feedback page?",
"jokes-7": "HSL has automatically backed up all servers 114514 times, the total space used exceeds 1000000 TB, has automatically transferred to another dimension space, do you want to read?"
"jokes-7": "HSL has automatically backed up all servers 114514 times, the total space used exceeds 1000000 TB, has automatically transferred to another dimension space, do you want to read?",
"hsl-shell-introduction": "HSL command line usage guide:\nEnter 'hsl' to open the function menu, and other text will be sent to the server. If your server has a 'hsl' command, please go to the Java pop-up window or game to input.",
"hsl-shell-introduction-prompt-select": "This text will only be displayed once. Please read and understand.",
"process-functions": "Server Process Functions",
"process-functions-kill": "Kill Process",
"process-functions-sigstop": "Suspend Process",
"process-functions-sigcont": "Resume Process",
"process-functions-prompt-select": "Please select the process function to be executed:",
"process-functions-kill-prompt-confirm": "Are you sure you want to kill the process?",
"process-functions-kill-success": "[bold green]The process has been killed successfully.",
"process-functions-sigstop-prompt-confirm": "Are you sure you want to suspend the process?",
"process-functions-sigstop-success": "[bold green]The process has been suspended successfully.",
"process-functions-sigcont-prompt-confirm": "Are you sure you want to resume the process?",
"process-functions-sigcont-success": "[bold green]The process has been resumed successfully."
}
Loading

0 comments on commit cd3d91a

Please sign in to comment.