Room and lobby chat, /host, /fashion, player-colored lines, context tips, and American spelling

- Chat goes to your room (the sandbox, or your game) by default, and to the whole lobby with /all or a leading !.
  Lines from elsewhere say where they came from ([Sandbox], [Game 2]), lobby-wide ones from your room say [All].
  Typing shows only to your room. Protocol 8.
- Hosting: everyone is told who hosts now, and the host can hand it over with /host "player".
- /fashion <item> wears a fashion locally, like /color.
- Lines that mention a player show in that player's color, looked up as they draw.
- Tips are tagged with the item and situations they fit, and those that fit are 1.5x as likely. They turn over every
  10 seconds on one continuous run, show under the menu too, and can be turned off with Tips in HUD / Gameplay. The
  HUD hotbars hide while the menu is up (except on its item tabs).
- American spelling throughout: color, center, gray, mustache, and so on.
This commit is contained in:
shweet committed 2026-11-10 19:37:19 -05:00
1 parent f4e7784f3c
commit 93cdf46d5a
38 files changed
+603 -325

No files matched your search

+45 -12
View File
@@ -427,24 +427,56 @@ static void text_handle(int id, const std::string& text)
std::printf("Lobby %s: %d kicked %d\n", code.c_str(), id, (int)target);
return lobby_exit((int)target, LEAVE_KICKED, reason);
}
if (type == "host")
{
// Only a lobby's host can hand hosting over, and only to someone else in their lobby, who moves to the front
// (the first member hosts).
int64_t target = 0;
std::string& code = members[id].code;
if (message["id"].get(target) || code.empty() || lobbies[code].members.front() != id || target == id)
return error_send(id, "Only the host can hand over hosting");
std::vector<int>& order = lobbies[code].members;
auto found = std::find(order.begin(), order.end(), (int)target);
if (found == order.end())
return error_send(id, "No such player in the lobby");
std::rotate(order.begin(), found, found + 1);
std::printf("Lobby %s: %d made %d host\n", code.c_str(), id, (int)target);
return lobby_send(code, "{\"type\":\"host_change\",\"host\":" + std::to_string(target) + "}");
}
error_send(id, "Unknown message");
}
// Text a member sent for the lobby (a chat line or their new name), cleaned, passed to everyone with who it's from.
static void text_relay(int id, PacketType type, PacketReader& reader, size_t max)
// Sends to whoever a member's words reach: their room (everyone playing or watching where they are), or with
// CHAT_LOBBY (or no room yet), the whole lobby.
static void scope_send(int id, ChatScope scope, const std::string& bytes)
{
Member& member = members[id];
for (int other : lobbies[member.code].members)
if (scope == CHAT_LOBBY || member.room == 0 || members[other].room == member.room)
send(other, bytes, DELIVERY_RELIABLE);
}
// Text a member sent (a chat line or their new name), cleaned, passed on with who it's from: a new name to the whole
// lobby, and a chat line, with the room its sender is in, as far as its scope reaches.
static void text_relay(int id, PacketType type, PacketReader& reader)
{
Uint8 scope = CHAT_LOBBY;
Uint16 length = 0;
if (!reader.get(length) || reader.at + length > reader.bytes.size())
if ((type == PACKET_CHAT && !reader.get(scope)) || !reader.get(length) || reader.at + length > reader.bytes.size())
return;
std::string text = text_clean(std::string_view(reader.bytes.data() + reader.at, length), max);
std::string text = text_clean(std::string_view(reader.bytes.data() + reader.at, length),
type == PACKET_CHAT ? CHAT_MESSAGE_MAX : PLAYER_NAME_MAX);
if (text.empty())
return;
if (type == PACKET_RENAME)
members[id].name = text;
PacketWriter packet;
packet.put(type).put(id).put((Uint16)text.size());
packet.put(type).put(id);
if (type == PACKET_CHAT)
packet.put(scope).put(members[id].room);
else
members[id].name = text;
packet.put((Uint16)text.size());
packet.bytes += text;
lobby_send(members[id].code, packet.bytes, DELIVERY_RELIABLE);
scope_send(id, scope == CHAT_ROOM ? CHAT_ROOM : CHAT_LOBBY, packet.bytes);
}
// The game messages, in binary (see protocol.h), from a member in a lobby.
@@ -507,12 +539,13 @@ static void packet_handle(int id, const std::string& bytes)
return;
}
if (type == PACKET_CHAT || type == PACKET_RENAME)
return text_relay(id, type, reader, type == PACKET_CHAT ? CHAT_MESSAGE_MAX : PLAYER_NAME_MAX);
return text_relay(id, type, reader);
if (type == PACKET_TYPING)
{
Uint8 isTyping = 0;
// Only the room sees the "..." (a line said to all shows up elsewhere without one).
if (reader.get(isTyping))
lobby_send(member.code, PacketWriter().put(PACKET_TYPING).put(id).put(isTyping).bytes, DELIVERY_RELIABLE);
scope_send(id, CHAT_ROOM, PacketWriter().put(PACKET_TYPING).put(id).put(isTyping).bytes);
}
if (type == PACKET_WATCH)
{
@@ -587,7 +620,7 @@ static void room_step(Room& room)
}
// A sandbox's queued game that's ready starts in a room of its own: its players leave the sandbox (bringing nothing
// along but their name and colour) and go into it.
// along but their name and color) and go into it.
static void game_open(int sandboxSerial, int queuer)
{
Room& sandbox = rooms[sandboxSerial];
@@ -666,7 +699,7 @@ static void game_follow(int serial)
}
// Tells a lobby's members who's in each of its rooms (the sandbox, then its games): each room's mode and the players
// still in it with their colours, for the lobby page's lists.
// still in it with their colors, for the lobby page's lists.
static void rooms_send(const std::string& code)
{
std::vector<int>& lobbyRooms = lobbies[code].rooms;