diff --git a/client/src/components/lobby/LobbyChat.vue b/client/src/components/lobby/LobbyChat.vue
index bacfd265f..2d32547b5 100644
--- a/client/src/components/lobby/LobbyChat.vue
+++ b/client/src/components/lobby/LobbyChat.vue
@@ -6,7 +6,7 @@
:key="msg.username + msg.dateCreated"
class="mt-2 backdrop rounded p-1"
>
-
+
{{ msg.username }}:
{{ msg.message }}
diff --git a/server/src/entity/LobbyChatMessage.ts b/server/src/entity/LobbyChatMessage.ts
new file mode 100644
index 000000000..3d53b47be
--- /dev/null
+++ b/server/src/entity/LobbyChatMessage.ts
@@ -0,0 +1,27 @@
+import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
+import { GameType } from "@port-of-mars/shared/types";
+import { User } from "./User";
+
+@Entity()
+export class LobbyChatMessage {
+ @PrimaryGeneratedColumn()
+ id!: number;
+
+ @Column()
+ dateCreated!: Date;
+
+ @ManyToOne(type => User)
+ user!: User;
+
+ @Column()
+ userId!: number;
+
+ @Column()
+ message!: string;
+
+ @Column()
+ roomId!: string;
+
+ @Column()
+ lobbyType!: GameType;
+}
diff --git a/server/src/migration/1699482607231-AddLobbyChatMessage.ts b/server/src/migration/1699482607231-AddLobbyChatMessage.ts
new file mode 100644
index 000000000..e7cb13425
--- /dev/null
+++ b/server/src/migration/1699482607231-AddLobbyChatMessage.ts
@@ -0,0 +1,16 @@
+import {MigrationInterface, QueryRunner} from "typeorm";
+
+export class AddLobbyChatMessage1699482607231 implements MigrationInterface {
+ name = 'AddLobbyChatMessage1699482607231'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE "lobby_chat_message" ("id" SERIAL NOT NULL, "dateCreated" TIMESTAMP NOT NULL, "userId" integer NOT NULL, "message" character varying NOT NULL, "roomId" character varying NOT NULL, "lobbyType" character varying NOT NULL, CONSTRAINT "PK_4640a139c74b0d188cc5efdda33" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`ALTER TABLE "lobby_chat_message" ADD CONSTRAINT "FK_96a0bc5d48c597dbd5ea6400feb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "lobby_chat_message" DROP CONSTRAINT "FK_96a0bc5d48c597dbd5ea6400feb"`);
+ await queryRunner.query(`DROP TABLE "lobby_chat_message"`);
+ }
+
+}
diff --git a/server/src/rooms/lobby/common/index.ts b/server/src/rooms/lobby/common/index.ts
index c94a8e6e8..12b21c74a 100644
--- a/server/src/rooms/lobby/common/index.ts
+++ b/server/src/rooms/lobby/common/index.ts
@@ -51,6 +51,12 @@ export abstract class LobbyRoom extends Ro
onDispose() {
logger.trace(`Disposing of ${this.roomName} ${this.roomId}, no connected clients`);
+ logger.debug("Saving all lobby chat messages");
+ const chatMessages = [...this.state.chat];
+ logger.debug("lobby chat messages %s", chatMessages);
+ if (chatMessages) {
+ getServices().tournament.saveLobbyChatMessages(this.roomId, "tournament", chatMessages);
+ }
}
async getMaxConnections() {
@@ -152,7 +158,7 @@ export abstract class LobbyRoom extends Ro
logger.trace(`client ${client.auth.username} attempted to send a chat message while muted`);
return;
}
- this.state.addChatMessage(client.auth.username, message.value);
+ this.state.addChatMessage(client.auth, message.value);
});
this.onMessage("accept-invitation", (client: Client, message: AcceptInvitation) => {
this.onAcceptInvitation(client, message);
diff --git a/server/src/rooms/lobby/common/state.ts b/server/src/rooms/lobby/common/state.ts
index 34a113c6d..cf68c6552 100644
--- a/server/src/rooms/lobby/common/state.ts
+++ b/server/src/rooms/lobby/common/state.ts
@@ -1,6 +1,7 @@
import { Schema, ArraySchema, type } from "@colyseus/schema";
import { Client } from "colyseus";
import { LobbyChatMessageData } from "@port-of-mars/shared/types";
+import { User } from "@port-of-mars/server/entity";
/**
* Connected client with additional metadata
@@ -30,11 +31,13 @@ export class LobbyClient extends Schema {
export class LobbyChatMessage extends Schema implements LobbyChatMessageData {
constructor(msg: LobbyChatMessageData) {
super();
+ this.userId = msg.userId;
this.username = msg.username;
this.message = msg.message;
this.dateCreated = msg.dateCreated;
}
+ @type("number") userId: number;
@type("string") username: string;
@type("string") message: string;
@type("number") dateCreated: number;
@@ -90,9 +93,10 @@ export abstract class LobbyRoomState extends Schema {
}
}
- addChatMessage(username: string, message: string) {
+ addChatMessage(user: User, message: string) {
const data = {
- username,
+ userId: user.id,
+ username: user.username,
message,
dateCreated: new Date().getTime(),
};
diff --git a/server/src/services/tournament.ts b/server/src/services/tournament.ts
index 88b0e3c4d..e1d9d9d62 100644
--- a/server/src/services/tournament.ts
+++ b/server/src/services/tournament.ts
@@ -1,3 +1,4 @@
+import { MoreThanOrEqual, Not, SelectQueryBuilder } from "typeorm";
import {
User,
Player,
@@ -7,12 +8,13 @@ import {
Treatment,
Game,
} from "@port-of-mars/server/entity";
-import { MoreThanOrEqual, Not, SelectQueryBuilder } from "typeorm";
import { getServices } from "@port-of-mars/server/services";
import { BaseService } from "@port-of-mars/server/services/db";
+import { LobbyChatMessage } from "@port-of-mars/server/entity/LobbyChatMessage";
import { TournamentRoundDate } from "@port-of-mars/server/entity/TournamentRoundDate";
import {
GameType,
+ LobbyChatMessageData,
MarsEventOverride,
TournamentRoundInviteStatus,
TournamentStatus,
@@ -290,8 +292,8 @@ export class TournamentService extends BaseService {
.where("tournament.id = :tournamentId", { tournamentId })
.getOne();
- return tournament ?
- tournament.treatments.sort((a: Treatment, b: Treatment) => a.id - b.id)
+ return tournament
+ ? tournament.treatments.sort((a: Treatment, b: Treatment) => a.id - b.id)
: [];
}
@@ -418,4 +420,27 @@ export class TournamentService extends BaseService {
const offset = await getServices().settings.tournamentLobbyOpenAfterOffset();
return offset * 60 * 1000;
}
+
+ /**
+ * FIXME: might move this into a separate service at some point
+ * @param roomId
+ * @param messages
+ */
+ async saveLobbyChatMessages(
+ roomId: string,
+ roomType: GameType,
+ messages: Array
+ ) {
+ const repository = this.em.getRepository(LobbyChatMessage);
+ const lobbyChatMessages = messages.map(message =>
+ repository.create({
+ roomId,
+ lobbyType: roomType,
+ message: message.message,
+ dateCreated: new Date(message.dateCreated),
+ userId: message.userId,
+ })
+ );
+ await repository.save(lobbyChatMessages);
+ }
}
diff --git a/shared/src/types.ts b/shared/src/types.ts
index ff4c7d1a9..cf7d3a27a 100644
--- a/shared/src/types.ts
+++ b/shared/src/types.ts
@@ -68,6 +68,7 @@ export type GameType = "freeplay" | "tournament";
export type LobbyType = GameType;
export interface LobbyChatMessageData {
+ userId: number;
username: string;
message: string;
dateCreated: number;