Compare commits
3 Commits
f75adac97a
...
a03b4b0b93
| Author | SHA1 | Date | |
|---|---|---|---|
| a03b4b0b93 | |||
| e6e32033ad | |||
| 16b5209338 |
33
cvars.py
33
cvars.py
@ -119,6 +119,39 @@ COMMANDS = [
|
|||||||
'killserver',
|
'killserver',
|
||||||
'quit',
|
'quit',
|
||||||
'team',
|
'team',
|
||||||
|
# Match control
|
||||||
|
'readyall',
|
||||||
|
'allready',
|
||||||
|
'abort',
|
||||||
|
'pause',
|
||||||
|
'unpause',
|
||||||
|
'lock',
|
||||||
|
'unlock',
|
||||||
|
'timeout',
|
||||||
|
'timein',
|
||||||
|
# Player management
|
||||||
|
'shuffle',
|
||||||
|
'put',
|
||||||
|
'mute',
|
||||||
|
'unmute',
|
||||||
|
'slap',
|
||||||
|
'slay',
|
||||||
|
# Server control
|
||||||
|
'restart',
|
||||||
|
'endgame',
|
||||||
|
'nextmap',
|
||||||
|
'forcemap',
|
||||||
|
# QLX commands
|
||||||
|
'qlx',
|
||||||
|
'elo',
|
||||||
|
'balance',
|
||||||
|
'teams',
|
||||||
|
'scores',
|
||||||
|
# Info commands
|
||||||
|
'serverinfo',
|
||||||
|
'players',
|
||||||
|
'maplist',
|
||||||
|
'configstrings',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Bot names for addbot command (complete list)
|
# Bot names for addbot command (complete list)
|
||||||
|
|||||||
@ -94,6 +94,7 @@ class EventParser:
|
|||||||
# Get Match Time
|
# Get Match Time
|
||||||
if 'TIME' in data:
|
if 'TIME' in data:
|
||||||
self.game_state.server_info.match_time = int(data['TIME'])
|
self.game_state.server_info.match_time = int(data['TIME'])
|
||||||
|
self.game_state.server_info.match_time_last_sync = time.time()
|
||||||
|
|
||||||
if 'KILLER' not in data:
|
if 'KILLER' not in data:
|
||||||
return None
|
return None
|
||||||
@ -138,6 +139,7 @@ class EventParser:
|
|||||||
# Get Match Time
|
# Get Match Time
|
||||||
if 'TIME' in data:
|
if 'TIME' in data:
|
||||||
self.game_state.server_info.match_time = int(data['TIME'])
|
self.game_state.server_info.match_time = int(data['TIME'])
|
||||||
|
self.game_state.server_info.match_time_last_sync = time.time()
|
||||||
|
|
||||||
if 'VICTIM' not in data:
|
if 'VICTIM' not in data:
|
||||||
return None
|
return None
|
||||||
@ -244,6 +246,7 @@ class EventParser:
|
|||||||
# Get Match Time
|
# Get Match Time
|
||||||
if 'TIME' in data:
|
if 'TIME' in data:
|
||||||
self.game_state.server_info.match_time = int(data['TIME'])
|
self.game_state.server_info.match_time = int(data['TIME'])
|
||||||
|
self.game_state.server_info.match_time_last_sync = time.time()
|
||||||
|
|
||||||
team_won = data.get('TEAM_WON')
|
team_won = data.get('TEAM_WON')
|
||||||
round_num = data.get('ROUND', 0)
|
round_num = data.get('ROUND', 0)
|
||||||
@ -265,6 +268,7 @@ class EventParser:
|
|||||||
# Get Match Time
|
# Get Match Time
|
||||||
if 'TIME' in data:
|
if 'TIME' in data:
|
||||||
self.game_state.server_info.match_time = int(data['TIME'])
|
self.game_state.server_info.match_time = int(data['TIME'])
|
||||||
|
self.game_state.server_info.match_time_last_sync = time.time()
|
||||||
|
|
||||||
name = data.get('NAME', 'Unknown')
|
name = data.get('NAME', 'Unknown')
|
||||||
medal = data.get('MEDAL', 'UNKNOWN')
|
medal = data.get('MEDAL', 'UNKNOWN')
|
||||||
@ -299,6 +303,7 @@ class EventParser:
|
|||||||
# Get Match Time
|
# Get Match Time
|
||||||
if 'TIME' in data:
|
if 'TIME' in data:
|
||||||
self.game_state.server_info.match_time = int(data['TIME'])
|
self.game_state.server_info.match_time = int(data['TIME'])
|
||||||
|
self.game_state.server_info.match_time_last_sync = time.time()
|
||||||
|
|
||||||
if self.game_state.server_info.is_team_mode():
|
if self.game_state.server_info.is_team_mode():
|
||||||
return f"^8^2[GAME ON]^0 ^7Match has started - ^1^8RED ^0^7vs. ^4^8BLUE\n"
|
return f"^8^2[GAME ON]^0 ^7Match has started - ^1^8RED ^0^7vs. ^4^8BLUE\n"
|
||||||
@ -320,6 +325,7 @@ class EventParser:
|
|||||||
# Get Match Time
|
# Get Match Time
|
||||||
if 'TIME' in data:
|
if 'TIME' in data:
|
||||||
self.game_state.server_info.match_time = int(data['TIME'])
|
self.game_state.server_info.match_time = int(data['TIME'])
|
||||||
|
self.game_state.server_info.match_time_last_sync = time.time()
|
||||||
|
|
||||||
if not self.game_state.server_info.is_team_mode():
|
if not self.game_state.server_info.is_team_mode():
|
||||||
return None
|
return None
|
||||||
|
|||||||
1
state.py
1
state.py
@ -34,6 +34,7 @@ class ServerInfo:
|
|||||||
self.dead_players = {}
|
self.dead_players = {}
|
||||||
self.round_end_time = None
|
self.round_end_time = None
|
||||||
self.match_time = 0
|
self.match_time = 0
|
||||||
|
self.match_time_last_sync = 0 # Timestamp of last TIME update from server
|
||||||
|
|
||||||
def is_team_mode(self):
|
def is_team_mode(self):
|
||||||
"""Check if current gametype is a team mode"""
|
"""Check if current gametype is a team mode"""
|
||||||
|
|||||||
26
ui.py
26
ui.py
@ -9,6 +9,7 @@ import curses.textpad
|
|||||||
import threading
|
import threading
|
||||||
import queue
|
import queue
|
||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
from config import COLOR_PAIRS, INFO_WINDOW_HEIGHT, INFO_WINDOW_Y, OUTPUT_WINDOW_Y, INPUT_WINDOW_HEIGHT, TEAM_MODES, MAX_COMMAND_HISTORY
|
from config import COLOR_PAIRS, INFO_WINDOW_HEIGHT, INFO_WINDOW_Y, OUTPUT_WINDOW_Y, INPUT_WINDOW_HEIGHT, TEAM_MODES, MAX_COMMAND_HISTORY
|
||||||
from cvars import autocomplete, COMMAND_SIGNATURES, get_signature_with_highlight, get_argument_suggestions, COMMAND_ARGUMENTS
|
from cvars import autocomplete, COMMAND_SIGNATURES, get_signature_with_highlight, get_argument_suggestions, COMMAND_ARGUMENTS
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ def update_autocomplete_display(window, current_input, first_word, words, ends_w
|
|||||||
else:
|
else:
|
||||||
window.addstr(1, x_pos, arg_text, curses.A_DIM)
|
window.addstr(1, x_pos, arg_text, curses.A_DIM)
|
||||||
x_pos += len(arg_text) + 1
|
x_pos += len(arg_text) + 1
|
||||||
except:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# User is typing arguments
|
# User is typing arguments
|
||||||
@ -134,7 +135,7 @@ def update_autocomplete_display(window, current_input, first_word, words, ends_w
|
|||||||
match_line = f'<{arg_type}>: {" ".join(display_suggestions)}{more_indicator}'
|
match_line = f'<{arg_type}>: {" ".join(display_suggestions)}{more_indicator}'
|
||||||
try:
|
try:
|
||||||
window.addstr(1, 0, match_line, curses.A_DIM)
|
window.addstr(1, 0, match_line, curses.A_DIM)
|
||||||
except:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
suggestions = arg_suggestions # Store for Tab cycling
|
suggestions = arg_suggestions # Store for Tab cycling
|
||||||
suggestion_index = -1
|
suggestion_index = -1
|
||||||
@ -151,7 +152,7 @@ def update_autocomplete_display(window, current_input, first_word, words, ends_w
|
|||||||
else:
|
else:
|
||||||
window.addstr(1, x_pos, arg_text, curses.A_DIM)
|
window.addstr(1, x_pos, arg_text, curses.A_DIM)
|
||||||
x_pos += len(arg_text) + 1
|
x_pos += len(arg_text) + 1
|
||||||
except:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
elif first_word in COMMAND_SIGNATURES and COMMAND_SIGNATURES[first_word]:
|
elif first_word in COMMAND_SIGNATURES and COMMAND_SIGNATURES[first_word]:
|
||||||
@ -166,7 +167,7 @@ def update_autocomplete_display(window, current_input, first_word, words, ends_w
|
|||||||
else:
|
else:
|
||||||
window.addstr(1, x_pos, arg_text, curses.A_DIM)
|
window.addstr(1, x_pos, arg_text, curses.A_DIM)
|
||||||
x_pos += len(arg_text) + 1
|
x_pos += len(arg_text) + 1
|
||||||
except:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -180,7 +181,7 @@ def update_autocomplete_display(window, current_input, first_word, words, ends_w
|
|||||||
match_line = ' '.join(suggestions)
|
match_line = ' '.join(suggestions)
|
||||||
try:
|
try:
|
||||||
window.addstr(1, 0, match_line, curses.A_DIM)
|
window.addstr(1, 0, match_line, curses.A_DIM)
|
||||||
except:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return suggestions, suggestion_index, original_word
|
return suggestions, suggestion_index, original_word
|
||||||
@ -367,7 +368,7 @@ class UIManager:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
window.addstr(1, 0, display_line, curses.A_DIM)
|
window.addstr(1, 0, display_line, curses.A_DIM)
|
||||||
except:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
window.move(0, cursor_pos)
|
window.move(0, cursor_pos)
|
||||||
@ -496,8 +497,7 @@ class UIManager:
|
|||||||
curses.doupdate() # Immediate screen update
|
curses.doupdate() # Immediate screen update
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import logging
|
logger.error(f'Input error: {e}')
|
||||||
logging.getLogger('ui').error(f'Input error: {e}')
|
|
||||||
# Log but continue - input thread should stay alive
|
# Log but continue - input thread should stay alive
|
||||||
|
|
||||||
window.move(0, cursor_pos)
|
window.move(0, cursor_pos)
|
||||||
@ -536,8 +536,14 @@ class UIManager:
|
|||||||
|
|
||||||
timer_display = ""
|
timer_display = ""
|
||||||
if server_info.match_time > 0 and not server_info.warmup:
|
if server_info.match_time > 0 and not server_info.warmup:
|
||||||
mins = server_info.match_time // 60
|
# Calculate live time: add elapsed seconds since last server update
|
||||||
secs = server_info.match_time % 60
|
current_time = server_info.match_time
|
||||||
|
if server_info.match_time_last_sync > 0:
|
||||||
|
elapsed = int(time.time() - server_info.match_time_last_sync)
|
||||||
|
current_time += elapsed
|
||||||
|
|
||||||
|
mins = current_time // 60
|
||||||
|
secs = current_time % 60
|
||||||
timer_display = f"^3^0Time:^8^7 {mins}:{secs:02d}^0"
|
timer_display = f"^3^0Time:^8^7 {mins}:{secs:02d}^0"
|
||||||
else:
|
else:
|
||||||
timer_display = "^3^0Time:^8^7 0:00^0"
|
timer_display = "^3^0Time:^8^7 0:00^0"
|
||||||
|
|||||||
Reference in New Issue
Block a user