-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
309 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
src/main/java/com/github/athieriot/engine/GameProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package com.github.athieriot.engine; | ||
|
||
import akka.actor.AbstractActor; | ||
import akka.actor.Props; | ||
import akka.actor.Status; | ||
|
||
/** | ||
* Akka Actor whose responsibility is to handle Player action on a Kalah Game | ||
* | ||
* The main reason for this is to try to prevent a situation where two players/requests | ||
* would attempt to play at the same time. | ||
* | ||
* The benefit here is that each player's "Action" (A move) will be queued in the Actor message box | ||
* and processed one by one. One Game Engine is kept as a state per Actor and each moves will | ||
* be resolved as they arrived. Because actors are Singletons, thread safety is guaranteed. | ||
*/ | ||
public class GameProcessor extends AbstractActor { | ||
|
||
private Engine engine; | ||
|
||
static public Props props(Engine engine) { | ||
return Props.create(GameProcessor.class, () -> new GameProcessor(engine)); | ||
} | ||
|
||
static public class BoardState { } | ||
|
||
static public class PlayerAction { | ||
private int player; | ||
private int house; | ||
|
||
public PlayerAction(int player, int house) { | ||
this.player = player; | ||
this.house = house; | ||
} | ||
|
||
public int player() { | ||
return player; | ||
} | ||
|
||
public int house() { | ||
return house; | ||
} | ||
} | ||
|
||
public GameProcessor(Engine engine) { | ||
this.engine = engine; | ||
} | ||
|
||
@Override | ||
public Receive createReceive() { | ||
return receiveBuilder() | ||
.match(BoardState.class, s -> sender().tell(engine, getSelf())) | ||
.match(PlayerAction.class, action -> { | ||
try { | ||
engine.play(action.player(), action.house()); | ||
|
||
sender().tell(engine, getSelf()); | ||
} catch (Exception e) { | ||
sender().tell(new Status.Failure(e), getSelf()); | ||
} | ||
}).build(); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
src/main/java/com/github/athieriot/registry/ProcessorRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package com.github.athieriot.registry; | ||
|
||
import akka.actor.ActorNotFound; | ||
import akka.actor.ActorRef; | ||
import akka.actor.ActorSelection; | ||
import akka.actor.ActorSystem; | ||
import com.github.athieriot.engine.Engine; | ||
import com.github.athieriot.engine.GameProcessor; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.NoSuchElementException; | ||
import java.util.UUID; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
import static akka.pattern.PatternsCS.ask; | ||
import static com.github.athieriot.engine.GameProcessor.props; | ||
import static java.util.concurrent.TimeUnit.SECONDS; | ||
import static scala.concurrent.duration.FiniteDuration.apply; | ||
|
||
/** | ||
* Utility class to help deal with Processor Actors | ||
*/ | ||
@Component | ||
public class ProcessorRegistry { | ||
|
||
private final ActorSystem system; | ||
|
||
@Autowired | ||
public ProcessorRegistry(ActorSystem system) { | ||
this.system = system; | ||
} | ||
|
||
public void spawnGameProcessorFor(Engine engine) { | ||
system.actorOf(props(engine), engine.id().toString()); | ||
} | ||
|
||
public CompletableFuture<Engine> stateOf(UUID id) { | ||
return findProcessor(id).thenCompose(processor -> | ||
ask(processor, new GameProcessor.BoardState(), 5000L) | ||
.toCompletableFuture() | ||
.thenApply(o -> (Engine) o) | ||
); | ||
} | ||
|
||
public CompletableFuture<Engine> processPlayerAction(UUID id, int player, int house) { | ||
return findProcessor(id).thenCompose(processor -> | ||
ask(processor, new GameProcessor.PlayerAction(player, house), 5000L) | ||
.toCompletableFuture() | ||
.thenApply(o -> (Engine) o) | ||
); | ||
} | ||
|
||
private CompletableFuture<ActorRef> findProcessor(UUID id) { | ||
ActorSelection selection = system.actorSelection("/user/" + id.toString()); | ||
|
||
return resolveActor(selection); | ||
} | ||
|
||
private CompletableFuture<ActorRef> resolveActor(ActorSelection selection) { | ||
return selection.resolveOneCS(apply(2, SECONDS)) | ||
.toCompletableFuture() | ||
.exceptionally(this::convertActorNotFound); | ||
} | ||
|
||
private ActorRef convertActorNotFound(Throwable e) { | ||
if (e instanceof ActorNotFound) { | ||
throw new NoSuchElementException(); | ||
} | ||
|
||
throw new RuntimeException(e); | ||
} | ||
} |
27 changes: 0 additions & 27 deletions
27
src/main/java/com/github/athieriot/repository/EngineRepository.java
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.