Merge pull request #148 from aguibert/singleParty-fixes

Bug fixes from testing on 10/15
This commit is contained in:
Andrew Guibert 2018-10-16 10:49:32 -05:00 committed by GitHub
commit 33eaf14720
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 86 additions and 40 deletions

View File

@ -5,7 +5,7 @@
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
<attributes>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="build/classes/java/main"/>

View File

@ -357,7 +357,8 @@ export class ControlsComponent implements OnInit, OnDestroy {
).toPromise();
this.processRequeue(nextRoundID);
} else {
let queueCallback = new EventSourcePolyfill(`${environment.API_URL_PARTY}/${partyId}/queue`, {});
let playerId = sessionStorage.getItem('userId');
let queueCallback = new EventSourcePolyfill(`${environment.API_URL_PARTY}/${partyId}/queue?playerId=${playerId}`, {});
queueCallback.onmessage = msg => {
let queueMsg = JSON.parse(msg.data);
if (queueMsg.queuePosition) {

View File

@ -367,7 +367,8 @@ export class GameComponent implements OnInit, OnDestroy {
this.processRequeue(nextRoundID);
}
} else {
let queueCallback = new EventSourcePolyfill(`${environment.API_URL_PARTY}/${partyId}/queue`, {});
let playerId = sessionStorage.getItem('userId');
let queueCallback = new EventSourcePolyfill(`${environment.API_URL_PARTY}/${partyId}/queue?playerId=${playerId}`, {});
queueCallback.onmessage = msg => {
let queueMsg = JSON.parse(msg.data);
if (queueMsg.queuePosition) {
@ -455,6 +456,11 @@ export class GameComponent implements OnInit, OnDestroy {
this.waitingSub = this.waitingTimer.subscribe((t) => {
if (this.waitCard) {
this.waitCard.bodyString = `${seconds - t}`;
// If we have gotten down to -2, a connection was probably interrupted
if (-1 > (seconds - t)) {
this.waitingSub.unsubscribe();
this.connectionInterrupted();
}
}
});
@ -484,13 +490,17 @@ export class GameComponent implements OnInit, OnDestroy {
verifyOpen() {
if (!this.gameService.isOpen()) {
console.log('GameService socket not open');
this.ngZone.run(() => {
this.router.navigate(['/login']);
});
this.connectionInterrupted();
}
}
connectionInterrupted() {
alert('Connection to game-service interrrupted. Re-directing to login page.');
this.ngZone.run(() => {
this.router.navigate(['/login']);
});
}
addBorder(x, y, w, h) {
let shape: Shape = new Shape();
shape.shadow = new Shadow(Obstacle.COLOR, 0, 0, 20);

View File

@ -24,6 +24,7 @@ export class LoginComponent implements OnInit, OnDestroy {
player = new Player();
isFullDevice: boolean = !/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
isQuickPlayAllowed: boolean = this.isFullDevice;
isSingleParty: boolean = false;
isGoogleConfigured: boolean = false;
isGithubConfigured: boolean = false;
isTwitterConfigured: boolean = false;
@ -71,7 +72,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.showQueue(queuePosition);
}
this.checkForQuickPlay();
this.checkForSingleParty();
this.checkSsoOptions();
}
@ -109,32 +110,36 @@ export class LoginComponent implements OnInit, OnDestroy {
if (data.indexOf('GitHubAuth') > -1) {
this.isGithubConfigured = true;
}
}
});
}
async checkForQuickPlay() {
if (this.isQuickPlayAllowed) {
console.log('Quick play is supported -- skipping party service check');
return;
async checkForSingleParty() {
if (this.isSingleParty) {
return;
}
let data: any = await this.http.get(`${environment.API_URL_PARTY}/describe`).toPromise();
if (data == null) {
console.log('WARNING: Unable to contact party service to determine if quick play is allowed');
return;
console.log('WARNING: Unable to contact party service to determine if single party mode is enabled');
return;
}
if (data.isSingleParty === true) {
console.log('Quick play is supported');
this.ngZone.run(() => {
this.isQuickPlayAllowed = true;
});
console.log('Single party mode enabled');
this.party = data.partyId;
this.ngZone.run(() => {
this.isSingleParty = true;
});
} else {
console.log('Quick play is NOT supported');
console.log('Single party mode disabled');
}
}
async quickJoin() {
if (this.isSingleParty) {
this.joinParty();
return;
}
// First get an unstarted round ID
let roundID = await this.http.get(`${environment.API_URL_GAME_ROUND}/available`, { responseType: 'text' }).toPromise();
// Then join the round
@ -248,7 +253,8 @@ export class LoginComponent implements OnInit, OnDestroy {
if (LoginComponent.queueCallback) {
LoginComponent.queueCallback.close();
}
LoginComponent.queueCallback = new EventSourcePolyfill(`${environment.API_URL_PARTY}/${this.party}/queue`, {});
let playerId = sessionStorage.getItem('userId');
LoginComponent.queueCallback = new EventSourcePolyfill(`${environment.API_URL_PARTY}/${this.party}/queue?playerId=${playerId}`, {});
this.setQueueOnMessage();
LoginComponent.queueCallback.onerror = msg => {
console.log('Error showing queue position: ' + JSON.stringify(msg.data));

View File

@ -6,7 +6,7 @@
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
<attributes>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="build/classes/java/main"/>

View File

@ -521,7 +521,7 @@ public class GameRound implements Runnable {
// Give players a 10s grace period before they are removed from a finished game
if (exec != null)
exec.schedule(() -> {
for (Session s : clients.keySet())
for (Session s : new HashSet<Session>(clients.keySet()))
removeClient(s);
}, 10, TimeUnit.SECONDS);
}

View File

@ -44,8 +44,8 @@ public class Party {
return this.currentRound;
}
public void enqueueClient(SseEventSink sink, Sse sse) {
queue.add(sink, sse);
public void enqueueClient(String playerId, SseEventSink sink, Sse sse) {
queue.add(playerId, sink, sse);
}
public void close() {

View File

@ -1,5 +1,6 @@
package org.libertybikes.game.party;
import java.util.Objects;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.ws.rs.core.MediaType;
@ -15,15 +16,18 @@ public class PartyQueue {
private final ConcurrentLinkedDeque<QueuedClient> waitingPlayers = new ConcurrentLinkedDeque<>();
private final Party party;
private int queueCounter = 0, firstClient = 0;
public PartyQueue(Party p) {
this.party = p;
}
public void add(SseEventSink sink, Sse sse) {
QueuedClient client = new QueuedClient(sink, sse);
party.log("Adding client " + client.queueNumber + " into the queue in position " + client.queuePosition());
public void add(String playerId, SseEventSink sink, Sse sse) {
QueuedClient client = new QueuedClient(playerId, sink, sse);
// If this client was already in the queue, remove them and add them at the end
if (waitingPlayers.removeFirstOccurrence(client)) {
party.log("Removed client " + playerId + " from queue before adding at end");
}
party.log("Adding client " + playerId + " into the queue in position " + client.queuePosition());
waitingPlayers.add(client);
if (party.getCurrentRound().isOpen())
promoteClients();
@ -38,7 +42,6 @@ public class PartyQueue {
QueuedClient first = waitingPlayers.pollFirst();
if (first != null) {
first.promoteToGame(newRound.id);
firstClient++;
}
}
for (QueuedClient client : waitingPlayers)
@ -53,27 +56,35 @@ public class PartyQueue {
}
private class QueuedClient {
private final int queueNumber;
private final String playerId;
private final SseEventSink sink;
private final Sse sse;
public QueuedClient(SseEventSink sink, Sse sse) {
public QueuedClient(String playerId, SseEventSink sink, Sse sse) {
this.playerId = playerId;
this.sink = sink;
this.sse = sse;
this.queueNumber = PartyQueue.this.queueCounter++;
}
public int queuePosition() {
return this.queueNumber - PartyQueue.this.firstClient + 1;
int position = 1;
for (QueuedClient c : PartyQueue.this.waitingPlayers) {
if (this.equals(c))
break;
else
position++;
}
return position;
}
public void notifyPosition() {
int position = queuePosition();
OutboundSseEvent event = sse.newEventBuilder()
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(new OutboundMessage.QueuePosition(queuePosition()))
.data(new OutboundMessage.QueuePosition(position))
.build();
sink.send(event);
party.log("Notified queued client " + queueNumber + " who is currently at position " + queuePosition());
party.log("Notified queued client " + playerId + " who is currently at position " + position);
}
public void promoteToGame(String roundId) {
@ -82,13 +93,25 @@ public class PartyQueue {
.data(new OutboundMessage.RequeueGame(roundId))
.build();
sink.send(event);
party.log("Promoted queued client " + queueNumber + " into round " + roundId);
party.log("Promoted queued client " + playerId + " into round " + roundId);
close();
}
public void close() {
sink.close();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof QueuedClient))
return false;
QueuedClient other = (QueuedClient) obj;
return Objects.equals(this.playerId, other.playerId);
}
}
}

View File

@ -7,6 +7,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@ -21,6 +22,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.sse.Sse;
@ -64,6 +66,7 @@ public class PartyService {
public Map<String, Object> describe() {
Map<String, Object> config = new HashMap<>();
config.put("isSingleParty", this.isSingleParty);
config.put("partyId", allParties.values().iterator().next().id);
return config;
}
@ -109,10 +112,13 @@ public class PartyService {
@GET
@Path("/{partyId}/queue")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void joinQueue(@PathParam("partyId") String partyId, @Context SseEventSink sink, @Context Sse sse) {
public void joinQueue(@PathParam("partyId") String partyId,
@QueryParam("playerId") String playerId,
@Context SseEventSink sink, @Context Sse sse) {
Objects.requireNonNull(playerId, "Client attemted to queue for a party without providing playerId");
Party p = getParty(partyId);
if (p != null)
p.enqueueClient(sink, sse);
p.enqueueClient(playerId, sink, sse);
}
}

View File

@ -6,7 +6,7 @@
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
<attributes>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="build/classes/java/main"/>