Merge pull request #1723 from thecodingmachine/fix-group

Change group radius management
This commit is contained in:
David Négrier 2022-01-14 13:53:15 +01:00 committed by GitHub
commit ccfe049e6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 133 additions and 36 deletions

View File

@ -213,13 +213,13 @@ export class GameRoom {
} }
private updateUserGroup(user: User): void { private updateUserGroup(user: User): void {
user.group?.updatePosition();
user.group?.searchForNearbyUsers();
if (user.silent) { if (user.silent) {
return; return;
} }
const group = user.group; const group = user.group;
const closestItem: User | Group | null = this.searchClosestAvailableUserOrGroup(user);
if (group === undefined) { if (group === undefined) {
// If the user is not part of a group: // If the user is not part of a group:
// should he join a group? // should he join a group?
@ -229,12 +229,11 @@ export class GameRoom {
return; return;
} }
const closestItem: User | Group | null = this.searchClosestAvailableUserOrGroup(user);
if (closestItem !== null) { if (closestItem !== null) {
if (closestItem instanceof Group) { if (closestItem instanceof Group) {
// Let's join the group! // Let's join the group!
closestItem.join(user); closestItem.join(user);
closestItem.setOutOfBounds(false);
} else { } else {
const closestUser: User = closestItem; const closestUser: User = closestItem;
const group: Group = new Group( const group: Group = new Group(
@ -249,32 +248,95 @@ export class GameRoom {
} }
} }
} else { } else {
// If the user is part of a group: let hasKickOutSomeone = false;
// should he leave the group? let followingMembers: User[] = [];
let noOneOutOfBounds = true;
group.getUsers().forEach((foreignUser: User) => { const previewNewGroupPosition = group.previewGroupPosition();
if (foreignUser.group === undefined) {
if (!previewNewGroupPosition) {
this.leaveGroup(user);
return; return;
} }
const usrPos = foreignUser.getPosition();
const grpPos = foreignUser.group.getPosition(); if (user.hasFollowers() || user.following) {
const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos); followingMembers = user.hasFollowers()
? group.getUsers().filter((currentUser) => currentUser.following === user)
: group.getUsers().filter((currentUser) => currentUser.following === user.following);
// If all group members are part of the same follow group
if (group.getUsers().length - 1 === followingMembers.length) {
let isOutOfBounds = false;
// If a follower is far away from the leader, "outOfBounds" is set to true
for (const member of followingMembers) {
const distance = GameRoom.computeDistanceBetweenPositions(
member.getPosition(),
previewNewGroupPosition
);
if (distance > this.groupRadius) { if (distance > this.groupRadius) {
if (foreignUser.hasFollowers() || foreignUser.following) { isOutOfBounds = true;
// If one user is out of the group bounds BUT following, the group still exists... but should be hidden. break;
// We put it in 'outOfBounds' mode }
group.setOutOfBounds(true); }
noOneOutOfBounds = false; group.setOutOfBounds(isOutOfBounds);
}
}
// Check if the moving user has kicked out another user
for (const headMember of group.getGroupHeads()) {
if (!headMember.group) {
this.leaveGroup(headMember);
continue;
}
const headPosition = headMember.getPosition();
const distance = GameRoom.computeDistanceBetweenPositions(headPosition, previewNewGroupPosition);
if (distance > this.groupRadius) {
hasKickOutSomeone = true;
break;
}
}
/**
* If the current moving user has kicked another user from the radius,
* the moving user leaves the group because he is too far away.
*/
const userDistance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), previewNewGroupPosition);
if (hasKickOutSomeone && userDistance > this.groupRadius) {
if (user.hasFollowers() && group.getUsers().length === 3 && followingMembers.length === 1) {
const other = group
.getUsers()
.find((currentUser) => !currentUser.hasFollowers() && !currentUser.following);
if (other) {
this.leaveGroup(other);
}
} else if (user.hasFollowers()) {
this.leaveGroup(user);
for (const member of followingMembers) {
this.leaveGroup(member);
}
// Re-create a group with the followers
const newGroup: Group = new Group(
this.roomUrl,
[user, ...followingMembers],
this.groupRadius,
this.connectCallback,
this.disconnectCallback,
this.positionNotifier
);
this.groups.add(newGroup);
} else { } else {
this.leaveGroup(foreignUser); this.leaveGroup(user);
} }
} }
});
if (noOneOutOfBounds && !user.group?.isEmpty()) {
group.setOutOfBounds(false);
}
} }
user.group?.updatePosition();
user.group?.searchForNearbyUsers();
} }
public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void {

View File

@ -59,6 +59,39 @@ export class Group implements Movable {
}; };
} }
/**
* Returns the list of users of the group, ignoring any "followers".
* Useful to compute the position of the group if a follower is "trapped" far away from the the leader.
*/
getGroupHeads(): User[] {
return Array.from(this.users).filter((user) => user.group?.leader === user || !user.following);
}
/**
* Preview the position of the group but don't update it
*/
previewGroupPosition(): { x: number; y: number } | undefined {
const users = this.getGroupHeads();
let x = 0;
let y = 0;
if (users.length === 0) {
return undefined;
}
users.forEach((user: User) => {
const position = user.getPosition();
x += position.x;
y += position.y;
});
x /= users.length;
y /= users.length;
return { x, y };
}
/** /**
* Computes the barycenter of all users (i.e. the center of the group) * Computes the barycenter of all users (i.e. the center of the group)
*/ */
@ -66,19 +99,15 @@ export class Group implements Movable {
const oldX = this.x; const oldX = this.x;
const oldY = this.y; const oldY = this.y;
let x = 0;
let y = 0;
// Let's compute the barycenter of all users. // Let's compute the barycenter of all users.
this.users.forEach((user: User) => { const newPosition = this.previewGroupPosition();
const position = user.getPosition();
x += position.x; if (!newPosition) {
y += position.y; return;
});
x /= this.users.size;
y /= this.users.size;
if (this.users.size === 0) {
throw new Error("EMPTY GROUP FOUND!!!");
} }
const { x, y } = newPosition;
this.x = x; this.x = x;
this.y = y; this.y = y;
@ -97,10 +126,12 @@ export class Group implements Movable {
if (!this.currentZone) return; if (!this.currentZone) return;
for (const user of this.positionNotifier.getAllUsersInSquareAroundZone(this.currentZone)) { for (const user of this.positionNotifier.getAllUsersInSquareAroundZone(this.currentZone)) {
// Todo: Merge two groups with a leader
if (user.group || this.isFull()) return; //we ignore users that are already in a group. if (user.group || this.isFull()) return; //we ignore users that are already in a group.
const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), this.getPosition()); const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), this.getPosition());
if (distance < this.groupRadius) { if (distance < this.groupRadius) {
this.join(user); this.join(user);
this.setOutOfBounds(false);
this.updatePosition(); this.updatePosition();
} }
} }
@ -176,4 +207,8 @@ export class Group implements Movable {
this.outOfBounds = true; this.outOfBounds = true;
} }
} }
get getOutOfBounds() {
return this.outOfBounds;
}
} }