Merge branch 'Story1117_Course_Boundary_Collision' into 'develop'

Checked if a boat has crossed the boundary/course limit, if so, bounce the boat back.

# Boundary crossing detection
* if a boat has crossed the boundary, it will be bounced back to where it has came from

# Testing
* Manual testing has been done.
* Boats have bounced back when collides with other boats, mark or boundary. 

#story[1117] #pair[hyi25, zyt10]

See merge request !60
This commit is contained in:
Michael Rausch
2017-08-16 17:25:14 +12:00
3 changed files with 81 additions and 21 deletions
+69 -11
View File
@@ -6,8 +6,12 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import seng302.gameServer.server.messages.BoatAction; import seng302.gameServer.server.messages.BoatAction;
import seng302.gameServer.server.messages.BoatStatus; import seng302.gameServer.server.messages.BoatStatus;
import seng302.gameServer.server.messages.MarkRoundingMessage; import seng302.gameServer.server.messages.MarkRoundingMessage;
@@ -16,6 +20,7 @@ import seng302.gameServer.server.messages.Message;
import seng302.gameServer.server.messages.RoundingBoatStatus; import seng302.gameServer.server.messages.RoundingBoatStatus;
import seng302.gameServer.server.messages.YachtEventCodeMessage; import seng302.gameServer.server.messages.YachtEventCodeMessage;
import seng302.model.GeoPoint; import seng302.model.GeoPoint;
import seng302.model.Limit;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.PolarTable; import seng302.model.PolarTable;
import seng302.model.ServerYacht; import seng302.model.ServerYacht;
@@ -23,6 +28,7 @@ import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.mark.MarkOrder; import seng302.model.mark.MarkOrder;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.utilities.XMLParser;
/** /**
* A Static class to hold information about the current state of the game (model) * A Static class to hold information about the current state of the game (model)
@@ -33,6 +39,7 @@ public class GameState implements Runnable {
@FunctionalInterface @FunctionalInterface
interface NewMessageListener { interface NewMessageListener {
void notify(Message message); void notify(Message message);
} }
@@ -59,6 +66,7 @@ public class GameState implements Runnable {
private static MarkOrder markOrder; private static MarkOrder markOrder;
private static long startTime; private static long startTime;
private static Set<Mark> marks; private static Set<Mark> marks;
private static List<Limit> courseLimit;
private static List<NewMessageListener> markListeners; private static List<NewMessageListener> markListeners;
@@ -81,7 +89,7 @@ public class GameState implements Runnable {
yachts = new HashMap<>(); yachts = new HashMap<>();
players = new ArrayList<>(); players = new ArrayList<>();
GameState.hostIpAddress = hostIpAddress; GameState.hostIpAddress = hostIpAddress;
;
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
isRaceStarted = false; isRaceStarted = false;
//set this when game stage changes to prerace //set this when game stage changes to prerace
@@ -95,6 +103,22 @@ public class GameState implements Runnable {
new Thread(this, "GameState").start(); //Run the auto updates on the game state new Thread(this, "GameState").start(); //Run the auto updates on the game state
marks = new MarkOrder().getAllMarks(); marks = new MarkOrder().getAllMarks();
setCourseLimit("/server_config/race.xml");
}
private void setCourseLimit(String url) {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder;
Document document = null;
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
document = documentBuilder.parse(new InputSource(getClass().getResourceAsStream(url)));
} catch (Exception e) {
// sorry, we have to catch general one, otherwise we have to catch five different exceptions.
logger.trace("Failed to load course limit for boundary collision detection.", e);
}
courseLimit = XMLParser.parseRace(document).getCourseLimit();
} }
public static String getHostIpAddress() { public static String getHostIpAddress() {
@@ -244,7 +268,7 @@ public class GameState implements Runnable {
yacht.runAutoPilot(); yacht.runAutoPilot();
yacht.updateLocation(timeInterval); yacht.updateLocation(timeInterval);
if (yacht.getBoatStatus() != BoatStatus.FINISHED) { if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
checkForCollision(yacht); checkCollision(yacht);
checkForLegProgression(yacht); checkForLegProgression(yacht);
raceFinished = false; raceFinished = false;
} }
@@ -255,9 +279,28 @@ public class GameState implements Runnable {
} }
} }
/**
* Check if the yacht has crossed the course limit
*
* @param yacht the yacht to be tested
* @return a boolean value of if there is a boundary collision
*/
private static Boolean checkBoundaryCollision(ServerYacht yacht) {
for (int i = 0; i < courseLimit.size() - 1; i++) {
if (GeoUtility.checkCrossedLine(courseLimit.get(i), courseLimit.get(i + 1),
yacht.getLastLocation(), yacht.getLocation()) != 0) {
return true;
}
}
if (GeoUtility.checkCrossedLine(courseLimit.get(courseLimit.size() - 1), courseLimit.get(0),
yacht.getLastLocation(), yacht.getLocation()) != 0) {
return true;
}
return false;
}
public static void checkForCollision(ServerYacht serverYacht) { public static void checkCollision(ServerYacht serverYacht) {
ServerYacht collidedYacht = checkCollision(serverYacht); ServerYacht collidedYacht = checkYachtCollision(serverYacht);
if (collidedYacht != null) { if (collidedYacht != null) {
GeoPoint originalLocation = serverYacht.getLocation(); GeoPoint originalLocation = serverYacht.getLocation();
serverYacht.setLocation( serverYacht.setLocation(
@@ -276,7 +319,7 @@ public class GameState implements Runnable {
new YachtEventCodeMessage(serverYacht.getSourceId()) new YachtEventCodeMessage(serverYacht.getSourceId())
); );
} else { } else {
Mark collidedMark = markCollidedWith(serverYacht); Mark collidedMark = checkMarkCollision(serverYacht);
if (collidedMark != null) { if (collidedMark != null) {
serverYacht.setLocation( serverYacht.setLocation(
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK) calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
@@ -287,6 +330,17 @@ public class GameState implements Runnable {
notifyMessageListeners( notifyMessageListeners(
new YachtEventCodeMessage(serverYacht.getSourceId()) new YachtEventCodeMessage(serverYacht.getSourceId())
); );
} else if (checkBoundaryCollision(serverYacht)) {
serverYacht.setLocation(
calculateBounceBack(serverYacht, serverYacht.getLocation(),
BOUNCE_DISTANCE_YACHT)
);
serverYacht.setCurrentVelocity(
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
);
notifyMessageListeners(
new YachtEventCodeMessage(serverYacht.getSourceId())
);
} }
} }
} }
@@ -353,6 +407,7 @@ public class GameState implements Runnable {
/** /**
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any * 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line * in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
*
* @param yacht the current yacht to check for progression * @param yacht the current yacht to check for progression
*/ */
private void checkForLegProgression(ServerYacht yacht) { private void checkForLegProgression(ServerYacht yacht) {
@@ -516,7 +571,7 @@ public class GameState implements Runnable {
} }
private static Mark markCollidedWith(ServerYacht yacht) { private static Mark checkMarkCollision(ServerYacht yacht) {
Set<Mark> marksInRace = GameState.getMarks(); Set<Mark> marksInRace = GameState.getMarks();
for (Mark mark : marksInRace) { for (Mark mark : marksInRace) {
if (GeoUtility.getDistance(yacht.getLocation(), mark) if (GeoUtility.getDistance(yacht.getLocation(), mark)
@@ -532,12 +587,14 @@ public class GameState implements Runnable {
* *
* @return The boats new position * @return The boats new position
*/ */
private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith, Double bounceDistance) { private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith,
Double heading = GeoUtility.getBearing(yacht.getLocation(), collidedWith); Double bounceDistance) {
Double heading = GeoUtility.getBearing(yacht.getLastLocation(), collidedWith);
// Invert heading // Invert heading
heading -= 180; heading -= 180;
Integer newHeading = Math.floorMod(heading.intValue(), 360); Integer newHeading = Math.floorMod(heading.intValue(), 360);
return GeoUtility.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance); return GeoUtility
.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance);
} }
/** /**
@@ -546,11 +603,12 @@ public class GameState implements Runnable {
* *
* @return yacht to compare to all other yachts. * @return yacht to compare to all other yachts.
*/ */
private static ServerYacht checkCollision(ServerYacht yacht) { private static ServerYacht checkYachtCollision(ServerYacht yacht) {
for (ServerYacht otherYacht : GameState.getYachts().values()) { for (ServerYacht otherYacht : GameState.getYachts().values()) {
if (otherYacht != yacht) { if (otherYacht != yacht) {
Double distance = GeoUtility.getDistance(otherYacht.getLocation(), yacht.getLocation()); Double distance = GeoUtility
.getDistance(otherYacht.getLocation(), yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) { if (distance < YACHT_COLLISION_DISTANCE) {
return otherYacht; return otherYacht;
} }
+1 -1
View File
@@ -60,7 +60,7 @@ public class ServerYacht extends Observable {
this.country = country; this.country = country;
this.sailIn = false; this.sailIn = false;
this.isAuto = false; this.isAuto = false;
this.location = new GeoPoint(57.670341, 11.826856); this.location = new GeoPoint(57.67046, 11.83751);
this.lastLocation = location; this.lastLocation = location;
this.heading = 120.0; //In degrees this.heading = 120.0; //In degrees
this.currentVelocity = 0d; //in mms-1 this.currentVelocity = 0d; //in mms-1
@@ -6,6 +6,8 @@ import org.junit.Test;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import static seng302.gameServer.GameState.checkCollision;
/** /**
* Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0 * Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0
* meters. * meters.
@@ -37,7 +39,7 @@ public class UpdateYachtTest {
if (!yacht1.getSailIn()) { if (!yacht1.getSailIn()) {
yacht1.toggleSailIn(); yacht1.toggleSailIn();
} }
GameState.checkForCollision(yacht1); checkCollision(yacht1);
double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1); double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1);
Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1); Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1);
} }
@@ -54,14 +56,14 @@ public class UpdateYachtTest {
if (!yacht1.getSailIn()) { if (!yacht1.getSailIn()) {
yacht1.toggleSailIn(); yacht1.toggleSailIn();
} }
GameState.checkForCollision(yacht1); checkCollision(yacht1);
Assert.assertTrue( Assert.assertTrue(
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2 GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
) )
); //Check that yachts are actually far enough apart for no collision. ); //Check that yachts are actually far enough apart for no collision.
Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 0.001); Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 1.001);
Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 0.001); Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 1.001);
Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 0.001); Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 1.001);
Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 0.001); Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 1.001);
} }
} }