Update Healer Man server

This commit is contained in:
phenom committed 2026-09-23 16:14:19 -04:00
1 parent f90ba7d989
commit 189b94d6b7
51 files changed
+3448 -154

No files matched your search

+24 -4
View File
@@ -699,16 +699,29 @@ export function openGameDatabase(options = {}) {
};
}
function onlineGroupCharacterSource(character) {
return character.classId.startsWith("rom-") || character.categoryId === "rom" ? "rom" : "wow";
}
function updateOnlineGroupMember(accountId, character, role) {
const group = groupMembership(accountId);
if (!group) throw new GameDatabaseError("Join or create a group first.", 409, "group_required");
if (!["tank", "healer", "damage"].includes(role)) {
throw new GameDatabaseError("Choose a valid party role.", 400, "invalid_party_role");
}
const selectedCharacter = normalizeGroupCharacter(character);
const otherMembers = database.prepare(`
SELECT character_json FROM online_group_members
WHERE group_id = ? AND account_id != ? AND character_json IS NOT NULL
`).all(group.id, accountId);
if (otherMembers.some((member) => onlineGroupCharacterSource(JSON.parse(member.character_json))
!== onlineGroupCharacterSource(selectedCharacter))) {
throw new GameDatabaseError("RoM characters cannot join a WoW group, or vice versa.", 409, "mixed_group_sources");
}
database.prepare(`
UPDATE online_group_members SET character_json = ?, role = ?
WHERE group_id = ? AND account_id = ?
`).run(JSON.stringify(normalizeGroupCharacter(character)), role, group.id, accountId);
`).run(JSON.stringify(selectedCharacter), role, group.id, accountId);
database.prepare("UPDATE online_groups SET updated_at = ? WHERE id = ?").run(Date.now(), group.id);
return onlineGroupState(accountId);
}
@@ -739,19 +752,26 @@ export function openGameDatabase(options = {}) {
if (members.some((member) => !member.character_json || !["tank", "healer", "damage"].includes(member.role))) {
throw new GameDatabaseError("Every player must select a character and party role before the group can start.", 409, "group_not_ready");
}
if (new Set(members.map((member) => onlineGroupCharacterSource(JSON.parse(member.character_json)))).size > 1) {
throw new GameDatabaseError("RoM characters cannot join a WoW group, or vice versa.", 409, "mixed_group_sources");
}
const romGroup = onlineGroupCharacterSource(JSON.parse(members[0].character_json)) === "rom";
const randomizedBiteSized = type === "dungeon"
&& contentId === "randomized-bite-sized-stormwind-stockade";
const fillWithAi = randomizedBiteSized || input?.fillWithAi !== false;
if (romGroup && randomizedBiteSized) {
throw new GameDatabaseError("This WoW-only activity is unavailable to RoM groups.", 409, "group_source_incompatible");
}
const fillWithAi = !romGroup && (randomizedBiteSized || input?.fillWithAi !== false);
if (input?.includeBetaSpecs !== undefined && typeof input.includeBetaSpecs !== "boolean") {
throw new GameDatabaseError("includeBetaSpecs must be boolean.", 400, "invalid_beta_preference");
}
const includeBetaSpecs = randomizedBiteSized ? false : input?.includeBetaSpecs === true;
const includeBetaSpecs = romGroup || randomizedBiteSized ? false : input?.includeBetaSpecs === true;
const requestedSize = randomizedBiteSized
? 5
: Math.max(1, Math.min(5, Math.trunc(Number(input?.partySize) || 5)));
const partySize = fillWithAi ? Math.max(members.length, requestedSize) : members.length;
const readiness = getCompanionSpecReadiness();
const companionSpecializationIds = Object.entries(readiness.config.statuses)
const companionSpecializationIds = romGroup ? [] : Object.entries(readiness.config.statuses)
.filter(([, status]) => status === "ready" || (includeBetaSpecs && status === "beta"))
.map(([specializationId]) => specializationId);
const activity = {
+31
View File
@@ -320,6 +320,37 @@ test("creates online groups through invitations and transfers leadership when th
assert.deepEqual(transferred.group.members.map((member) => member.username), ["GroupHealer"]);
}));
test("keeps RoM and WoW characters in separate online groups", () => withDatabase(async (database) => {
const leaderAuth = await database.register("SourceLeader", "group-pass");
const memberAuth = await database.register("SourceMember", "member-pass");
const leader = database.authenticate(leaderAuth.token);
const member = database.authenticate(memberAuth.token);
database.inviteOnlineGroupMember(leader, member.username);
const invite = database.onlineGroupState(member.id).invitations[0];
database.respondToOnlineGroupInvite(member.id, invite.id, true);
const wowCharacter = { id: "wow-priest", name: "Wow Priest", classId: "priest", categoryId: "wow", level: 20 };
const romCharacter = { id: "rom-priest", name: "RoM Priest", classId: "rom-priest", categoryId: "rom", level: 20 };
database.updateOnlineGroupMember(leader.id, wowCharacter, "healer");
assert.throws(() => database.updateOnlineGroupMember(member.id, romCharacter, "healer"),
(error) => error instanceof GameDatabaseError && error.code === "mixed_group_sources");
assert.equal(database.onlineGroupState(member.id).group.members[1].character, null);
database.updateOnlineGroupMember(leader.id, romCharacter, "healer");
assert.throws(() => database.updateOnlineGroupMember(member.id, wowCharacter, "healer"),
(error) => error instanceof GameDatabaseError && error.code === "mixed_group_sources");
database.updateOnlineGroupMember(member.id, { ...romCharacter, id: "rom-mage", name: "RoM Mage", classId: "rom-mage" }, "damage");
assert.throws(() => database.startOnlineGroupActivity(leader.id, {
type: "dungeon", contentId: "randomized-bite-sized-stormwind-stockade", fillWithAi: true, partySize: 5,
}), (error) => error instanceof GameDatabaseError && error.code === "group_source_incompatible");
const started = database.startOnlineGroupActivity(leader.id, {
type: "dungeon", contentId: "wailing-caverns", fillWithAi: true, partySize: 5,
});
assert.equal(started.group.activity.partySize, 2);
assert.equal(started.group.activity.fillWithAi, false);
assert.deepEqual(started.group.activity.companionSpecializationIds, []);
}));
test("launches players-only and AI-filled group activities only when every member is ready", () => withDatabase(async (database) => {
const leaderAuth = await database.register("ReadyLeader", "group-pass");
const memberAuth = await database.register("ReadyMember", "member-pass");