Improved server chat and screenmessage functionality

This commit is contained in:
Eric Froemling 2020-05-06 00:50:18 -07:00
parent 3872920093
commit c95357e3d0
10 changed files with 116 additions and 49 deletions

View File

@ -4132,16 +4132,16 @@
"assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c", "assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c",
"assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb", "assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb",
"assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe", "assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe",
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8c/a0/002b38837d6d538912c7e3353d38", "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6a/56/e48e9306428b4ae7b58225fce76c",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c8/af/0f496b474262aea9ee0f91145177", "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/dd/2d/bdd22cbd39a9bb27a75302ef837a",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/23/ff/2b90933ab022e851c9ee1885bd02", "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/61/f2/a679c5a3c9113c7b7599a59415fb",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/2f/ee/38fac529e6e1fc418334e3c5a96f", "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/6c/21/c80e03031e6c5959140588d439bf",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8d/99/1048ca7d9a3f8aabc7719399e996", "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/42/ff/6d9056500a768dae79ed44180796",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/81/b5/72bb876e9a7e88cbd2c828b7b1a9", "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/58/d4/3f6d3cb75988db046f22bfb5c2a5",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3d/8b/ff95157b4e62b80210841a81594b", "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a8/07/edf6360c8e5b824633bf55c97ed9",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/80/b7/ac34c7c602b40a1f4f095b2bd5df", "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/27/cc/0ec7cab492f111923320dc165427",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/c4/3b/8de9d3aa9d2bcfb7ad731b2243bc", "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/b2/b4/f68901c3dae4267a5640bf702d86",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/b9/e3/5f34b93ba4b62facbfea0a8dfde4", "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/ae/b7/0400bd1021d0fc0c40d1c2149364",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a7/3e/65b9f907e53f753aa4178500cb2f", "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/ee/6e/8f88fef729d85a07910cbedbac31",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/58/1f/3241179955da91425342cea435a0" "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e1/90/4877825fb2d779bd7ac9704a1a4c"
} }

View File

@ -275,6 +275,7 @@
<w>charmap</w> <w>charmap</w>
<w>charname</w> <w>charname</w>
<w>charstr</w> <w>charstr</w>
<w>chatmessage</w>
<w>checkarg</w> <w>checkarg</w>
<w>checkboxwidget</w> <w>checkboxwidget</w>
<w>checkenv</w> <w>checkenv</w>
@ -316,6 +317,7 @@
<w>cnode</w> <w>cnode</w>
<w>codecsmodule</w> <w>codecsmodule</w>
<w>codefilenames</w> <w>codefilenames</w>
<w>codehash</w>
<w>codeop</w> <w>codeop</w>
<w>collapsable</w> <w>collapsable</w>
<w>collidemodel</w> <w>collidemodel</w>
@ -800,6 +802,7 @@
<w>halign</w> <w>halign</w>
<w>handlemessage</w> <w>handlemessage</w>
<w>hant</w> <w>hant</w>
<w>hashfilename</w>
<w>hashlines</w> <w>hashlines</w>
<w>hashobj</w> <w>hashobj</w>
<w>hashopenssl</w> <w>hashopenssl</w>
@ -953,6 +956,7 @@
<w>langs</w> <w>langs</w>
<w>langtarget</w> <w>langtarget</w>
<w>langval</w> <w>langval</w>
<w>lasthash</w>
<w>lastline</w> <w>lastline</w>
<w>lastplayer</w> <w>lastplayer</w>
<w>lastpoweruptype</w> <w>lastpoweruptype</w>

View File

@ -34,7 +34,7 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand.
""" """
# (hash we can use to see if this file is out of date) # (hash we can use to see if this file is out of date)
# SOURCES_HASH=244232883360612053934546020264848527679 # SOURCES_HASH=303806386736755003110818846527806768186
# I'm sorry Pylint. I know this file saddens you. Be strong. # I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression # pylint: disable=useless-suppression
@ -1504,8 +1504,10 @@ def charstr(char_id: ba.SpecialChar) -> str:
return str() return str()
def chat_message(message: Union[str, ba.Lstr]) -> None: def chatmessage(message: Union[str, ba.Lstr],
"""chat_message(message: Union[str, ba.Lstr]) -> None clients: Sequence[int] = None) -> None:
"""chatmessage(message: Union[str, ba.Lstr],
clients: Sequence[int] = None) -> None
(internal) (internal)
""" """

View File

@ -31,8 +31,8 @@ from ba._freeforallsession import FreeForAllSession
from ba._dualteamsession import DualTeamSession from ba._dualteamsession import DualTeamSession
from bacommon.servermanager import (ServerCommand, StartServerModeCommand, from bacommon.servermanager import (ServerCommand, StartServerModeCommand,
ShutdownCommand, ShutdownReason, ShutdownCommand, ShutdownReason,
BroadcastCommand, ClientListCommand, ChatMessageCommand, ScreenMessageCommand,
KickCommand) ClientListCommand, KickCommand)
import _ba import _ba
if TYPE_CHECKING: if TYPE_CHECKING:
@ -58,9 +58,20 @@ def _cmd(command_data: bytes) -> None:
immediate=command.immediate) immediate=command.immediate)
return return
if isinstance(command, BroadcastCommand): if isinstance(command, ChatMessageCommand):
assert _ba.app.server is not None assert _ba.app.server is not None
_ba.app.server.broadcast_message(command.message) _ba.chatmessage(command.message, clients=command.clients)
return
if isinstance(command, ScreenMessageCommand):
assert _ba.app.server is not None
# Note: we have to do transient messages if
# clients is specified, so they won't show up
# in replays.
_ba.screenmessage(command.message,
color=command.color,
clients=command.clients,
transient=command.clients is not None)
return return
if isinstance(command, ClientListCommand): if isinstance(command, ClientListCommand):
@ -111,12 +122,6 @@ class ServerController:
timetype=TimeType.REAL, timetype=TimeType.REAL,
repeat=True) repeat=True)
def broadcast_message(self, message: str) -> None:
"""Broadcast a message to all connected clients."""
# FIXME: Should add a proper call for this, which would allow
# us to use Lstr values and colors and whatnot.
_ba.chat_message(message)
def print_client_list(self) -> None: def print_client_list(self) -> None:
"""Print info about all connected clients.""" """Print info about all connected clients."""
import json import json
@ -184,13 +189,13 @@ class ServerController:
self._executing_shutdown = True self._executing_shutdown = True
timestrval = time.strftime('%c') timestrval = time.strftime('%c')
if self._shutdown_reason is ShutdownReason.RESTARTING: if self._shutdown_reason is ShutdownReason.RESTARTING:
self.broadcast_message( _ba.screenmessage(Lstr(resource='internal.serverRestartingText'),
Lstr(resource='internal.serverRestartingText').evaluate()) color=(1, 0.5, 0.0))
print(f'{Clr.SBLU}Exiting for server-restart' print(f'{Clr.SBLU}Exiting for server-restart'
f' at {timestrval}{Clr.RST}') f' at {timestrval}{Clr.RST}')
else: else:
self.broadcast_message( _ba.screenmessage(Lstr(resource='internal.serverShuttingDownText'),
Lstr(resource='internal.serverShuttingDownText').evaluate()) color=(1, 0.5, 0.0))
print(f'{Clr.SBLU}Exiting for server-shutdown' print(f'{Clr.SBLU}Exiting for server-shutdown'
f' at {timestrval}{Clr.RST}') f' at {timestrval}{Clr.RST}')
with _ba.Context('ui'): with _ba.Context('ui'):

View File

@ -412,7 +412,7 @@ class PartyWindow(ba.Window):
self._popup_party_member_is_host = is_host self._popup_party_member_is_host = is_host
def _send_chat_message(self) -> None: def _send_chat_message(self) -> None:
_ba.chat_message(cast(str, ba.textwidget(query=self._text_field))) _ba.chatmessage(cast(str, ba.textwidget(query=self._text_field)))
ba.textwidget(edit=self._text_field, text='') ba.textwidget(edit=self._text_field, text='')
def close(self) -> None: def close(self) -> None:

View File

@ -44,7 +44,7 @@ from efro.dataclassutils import dataclass_assign, dataclass_validate
from bacommon.servermanager import (ServerConfig, StartServerModeCommand) from bacommon.servermanager import (ServerConfig, StartServerModeCommand)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Optional, List, Dict, Union from typing import Optional, List, Dict, Union, Tuple
from types import FrameType from types import FrameType
from bacommon.servermanager import ServerCommand from bacommon.servermanager import ServerCommand
@ -181,10 +181,31 @@ class ServerManagerApp:
# we'll hopefully still give it enough time to process/print. # we'll hopefully still give it enough time to process/print.
time.sleep(0.1) time.sleep(0.1)
def broadcast(self, message: str) -> None: def screenmessage(self,
"""Broadcast a message to all connected clients.""" message: str,
from bacommon.servermanager import BroadcastCommand color: Optional[Tuple[float, float, float]] = None,
self._enqueue_server_command(BroadcastCommand(message=message)) clients: Optional[List[int]] = None) -> None:
"""Display a screen-message.
This will have no name attached and not show up in chat history.
They will show up in replays, however (unless clients is passed).
"""
from bacommon.servermanager import ScreenMessageCommand
self._enqueue_server_command(
ScreenMessageCommand(message=message, color=color,
clients=clients))
def chatmessage(self,
message: str,
clients: Optional[List[int]] = None) -> None:
"""Send a chat message from the server.
This will have the server's name attached and will be logged
in client chat windows, just like other chat messages.
"""
from bacommon.servermanager import ChatMessageCommand
self._enqueue_server_command(
ChatMessageCommand(message=message, clients=clients))
def clientlist(self) -> None: def clientlist(self) -> None:
"""Print a list of connected clients.""" """Print a list of connected clients."""
@ -366,10 +387,16 @@ class ServerManagerApp:
# Watch for the process exiting. # Watch for the process exiting.
code: Optional[int] = self._process.poll() code: Optional[int] = self._process.poll()
if code is not None: if code is not None:
print(f'{Clr.CYN}Server process exited' if code == 0:
clr = Clr.CYN
slp = 0.0
else:
clr = Clr.SRED
slp = 5.0 # Avoid super fast death loops.
print(f'{clr}Server child-process exited'
f' with code {code}.{Clr.RST}') f' with code {code}.{Clr.RST}')
time.sleep(1.0) # Keep things from moving too fast.
self._reset_process_vars() self._reset_process_vars()
time.sleep(slp)
break break
time.sleep(0.25) time.sleep(0.25)

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND --> <!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-05-05 for Ballistica version 1.5.0 build 20002</em></h4> <h4><em>last updated on 2020-05-06 for Ballistica version 1.5.0 build 20003</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module, <p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p> which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr> <hr>
@ -3988,17 +3988,11 @@ cause the powerup box to make a sound and disappear or whatnot.</p>
</p> </p>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_ServerController____init__">&lt;constructor&gt;</a>, <a href="#method_ba_ServerController__broadcast_message">broadcast_message()</a>, <a href="#method_ba_ServerController__handle_transition">handle_transition()</a>, <a href="#method_ba_ServerController__kick">kick()</a>, <a href="#method_ba_ServerController__print_client_list">print_client_list()</a>, <a href="#method_ba_ServerController__shutdown">shutdown()</a></h5> <h5><a href="#method_ba_ServerController____init__">&lt;constructor&gt;</a>, <a href="#method_ba_ServerController__handle_transition">handle_transition()</a>, <a href="#method_ba_ServerController__kick">kick()</a>, <a href="#method_ba_ServerController__print_client_list">print_client_list()</a>, <a href="#method_ba_ServerController__shutdown">shutdown()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_ServerController____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_ServerController____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.ServerController(config: ServerConfig)</span></p> <p><span>ba.ServerController(config: ServerConfig)</span></p>
</dd>
<dt><h4><a name="method_ba_ServerController__broadcast_message">broadcast_message()</a></dt></h4><dd>
<p><span>broadcast_message(self, message: str) -&gt; None</span></p>
<p>Broadcast a message to all connected clients.</p>
</dd> </dd>
<dt><h4><a name="method_ba_ServerController__handle_transition">handle_transition()</a></dt></h4><dd> <dt><h4><a name="method_ba_ServerController__handle_transition">handle_transition()</a></dt></h4><dd>
<p><span>handle_transition(self) -&gt; bool</span></p> <p><span>handle_transition(self) -&gt; bool</span></p>

View File

@ -26,7 +26,7 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Optional from typing import Optional, Tuple, List
@dataclass @dataclass
@ -129,9 +129,18 @@ class ShutdownCommand(ServerCommand):
@dataclass @dataclass
class BroadcastCommand(ServerCommand): class ChatMessageCommand(ServerCommand):
"""Broadcast a message to all clients.""" """Chat message from the server."""
message: str message: str
clients: Optional[List[int]]
@dataclass
class ScreenMessageCommand(ServerCommand):
"""Screen-message from the server."""
message: str
color: Optional[Tuple[float, float, float]]
clients: Optional[List[int]]
@dataclass @dataclass

View File

@ -149,7 +149,6 @@ def run(cmd: str) -> None:
subprocess.run(cmd, shell=True, check=True) subprocess.run(cmd, shell=True, check=True)
# 1
def get_files_hash(filenames: Sequence[Union[str, Path]], def get_files_hash(filenames: Sequence[Union[str, Path]],
extrahash: str = '', extrahash: str = '',
int_only: bool = False, int_only: bool = False,

View File

@ -547,5 +547,32 @@ def printcolors() -> None:
f'{TerminalColor.RESET.value}') f'{TerminalColor.RESET.value}')
def lazy_increment_build() -> None:
"""Increment build number only if C++ sources have changed.
This is convenient to place in automatic commit/push scripts.
"""
import os
import subprocess
from efro.terminal import Clr
from efrotools import get_files_hash
from efrotools.code import get_code_filenames
codehash = get_files_hash(get_code_filenames(PROJROOT))
hashfilename = '.cache/lazy_increment_build'
try:
with open(hashfilename) as infile:
lasthash = infile.read()
except FileNotFoundError:
lasthash = ''
if codehash == lasthash:
pass
else:
print(f'{Clr.SMAG}Source(s) changed; incrementing build...{Clr.RST}')
subprocess.run(['tools/version_utils', 'incrementbuild'], check=True)
os.makedirs(os.path.dirname(hashfilename), exist_ok=True)
with open(hashfilename, 'w') as outfile:
outfile.write(codehash)
if __name__ == '__main__': if __name__ == '__main__':
snippets_main(globals()) snippets_main(globals())