Skip to content

Added timer-based & console-based announcement support#75

Open
kbarkevich wants to merge 1 commit intosepalani:devfrom
kbarkevich:server-announcements
Open

Added timer-based & console-based announcement support#75
kbarkevich wants to merge 1 commit intosepalani:devfrom
kbarkevich:server-announcements

Conversation

@kbarkevich
Copy link
Copy Markdown

This pull requests implements a basic server-wide announcement system, similar to that which Capcom used to display their "Capcom says:" messages.

  • One basic "thanks for playing" message is scheduled to dispatch every 30 minutes
  • Added broadcast_message function, callable from the --interactive console, to send any message to all online users

@kbarkevich kbarkevich changed the base branch from master to dev November 9, 2022 16:10
Copy link
Copy Markdown
Owner

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to test it against the Japanese version, asap. This version tended to have different char limit for message boxes.

Comment on lines +32 to +55
broadcast_segments = []
words = text.split(" ")

mod_words = []

for word in words:
curr_word = word
while len(curr_word) > 30:
mod_words.append(curr_word[0:30])
curr_word = curr_word[30:]
if len(curr_word) > 0:
mod_words.append(curr_word)

curr_broadcast = mod_words[0]
for word in mod_words[1:]:
if len(curr_broadcast) + 1 + len(word) <= 30:
curr_broadcast += " "
curr_broadcast += word
else:
broadcast_segments.append(curr_broadcast)
curr_broadcast = word
if len(curr_broadcast) > 0:
broadcast_segments.append(curr_broadcast)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Isn't there a simpler way to perform this? It looks like a lot of code for a simple task.
  • Didn't Capcom used newlines instead of creating a message for each line?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has to trim down lengthy words to segments that are at most the maximum character length, then do the math word-by-word to see if each one fits onto the current line when including a space. Those are 2 distinct processes, thus the 2 different for loops.

I DID completely forget to use the constant MAX_CHAR_LENGTH I defined though. So, oops.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't think Capcom did? The messages always showed up as distinct lines, each with their own sender name.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't think Capcom did? The messages always showed up as distinct lines, each with their own sender name.

That seems correct, they are sending 4 messages as part of their announcement as seen on these videos:

image
image

fmp_server.py Outdated
Comment on lines +60 to +62
info.text_color = pati.Long(0x00fffff0)
info.sender_id = pati.String("000000")
info.sender_name = pati.String("[MH3SP]")
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should probably be optional parameters too.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

info.sender_name = pati.String("[MH3SP]")
data += info.pack()
data += pati.lp2_string(broadcast_text)
print("BROADCAST: " + broadcast_text)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we log this message properly, somewhere, rather than using raw prints?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the time it gets to the FmpServer, it's already bundled up into a binary. This function doesn't have access to any loggers since it's not local to any of the PatServer classes.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this print necessary since the server could log any packets including broadcast ones?

master_server.py Outdated
Comment on lines +57 to +60
broadcast_queues = {"FmpServer": queue.Queue(), "OpnServer": queue.Queue(),
"LmpServer": queue.Queue(), "RfpServer": queue.Queue()}
threads = [
threading.Thread(target=server.serve_forever)
threading.Thread(target=server.serve_forever, name=server.__class__.__name__,
args=(broadcast_queues[server.__class__.__name__], ))
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you plan to use server.__class__.__name__ then you should also use it in broadcast_queues rather than hardcoding the (dict's key) string.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +67 to +71
def broadcast_message(text):
broadcast = FMP.construct_broadcast(text)
for broadcast_segment in broadcast:
broadcast_queues["FmpServer"].put(broadcast_segment, block=True)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should add a comment stating that it's for easier use in interactive mode. Moreover, you shouldn't use "FmpServer" string and prefer using .__class__.__name__ when possible.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

mh/server.py Outdated
if event == selectors.EVENT_WRITE:
try:
if handler.session.layer > 0: # in-game only
handler.send_packet(packet[0], packet[1], packet[2])
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If packet is a tuple of 3 elements, use the * operator instead: *packet.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@kbarkevich kbarkevich force-pushed the server-announcements branch from 88c4026 to 68e7942 Compare November 9, 2022 20:01
Addressed Sepalani comments

fixed issue where MAX_CHAR_LENGTH constant wasn't being used
@kbarkevich kbarkevich force-pushed the server-announcements branch from 23cb573 to 2e3f0fa Compare November 9, 2022 20:16
Copy link
Copy Markdown
Owner

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very quick review.


def construct_broadcast(text="Hello, World!", text_color=0x00fffff0,
sender_id="000000", sender_name="[MH3SP]"):
assert len(text) > 0
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert text should work.

Comment on lines +33 to +55
broadcast_segments = []
words = text.split(" ")

mod_words = []

for word in words:
curr_word = word
while len(curr_word) > MAX_CHAR_LENGTH:
mod_words.append(curr_word[0:MAX_CHAR_LENGTH])
curr_word = curr_word[MAX_CHAR_LENGTH:]
if len(curr_word) > 0:
mod_words.append(curr_word)

curr_broadcast = mod_words[0]
for word in mod_words[1:]:
if len(curr_broadcast) + 1 + len(word) <= MAX_CHAR_LENGTH:
curr_broadcast += " "
curr_broadcast += word
else:
broadcast_segments.append(curr_broadcast)
curr_broadcast = word
if len(curr_broadcast) > 0:
broadcast_segments.append(curr_broadcast)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't the module textwrap and textwrap.wrap be used instead?

info.sender_name = pati.String("[MH3SP]")
data += info.pack()
data += pati.lp2_string(broadcast_text)
print("BROADCAST: " + broadcast_text)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this print necessary since the server could log any packets including broadcast ones?

Comment on lines +66 to +70
def broadcast_message(text):
# Function for easier broadcasting in interactive mode.
broadcast = FMP.construct_broadcast(text)
for broadcast_segment in broadcast:
broadcast_queues[FMP.BASE.cls.__name__].put(broadcast_segment, block=True)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we iterate over the servers and call their broadcast_message method rather than "hijacking" their broadcast_queues?

Comment on lines +210 to +211
def serve_forever(self, broadcast_queue):
self.broadcast_queue = broadcast_queue
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing the broadcast_queue as a parameter seems overkill. Can't it be created in the server's constructor method?

with self.selector as selector:
selector.register(self, selectors.EVENT_READ)

event_watch = Timer()
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will do for the time being but a clock should be used. Having a single timer prevent handling multiple timed events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants