Skip to content

Commit

Permalink
enhance the game engine embeddable api #85
Browse files Browse the repository at this point in the history
  • Loading branch information
isuru89 committed Mar 10, 2021
1 parent ab69a90 commit 0f5cced
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 82 deletions.
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ data/statistics out of Redis.
Its very simple.

```java
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
// load the engine configuration (we use typesafe configs here)
var oasisConfigs = OasisConfigs.defaultConfigs();

Expand Down Expand Up @@ -98,23 +98,21 @@ public static void main(String[] args) {
engine.start();

// first notify game create and start events to game engine
engine.submit(GameCommand.create(gameId, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(gameId, GameCommand.GameLifecycle.START));
engine.createGame(gameId);

// first add game rules...
engine.submit(rule1);
engine.submit(rule2);
...
// let's parse the game rules from yaml file
var gameDef = GameParserYaml.fromFile(gameDefFile);
engine.startGame(gameId, gameDef);

// then you can submit events
// now you can submit events
engine.submit(event1);
engine.submit(event2);
...


// later, if you think the game is over, you can signal it to the engine
// so it will accepts no more events for that game and clean up anything related to the game.
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.REMOVE));
engine.stopGame(gameId);
}

```
Expand Down
26 changes: 22 additions & 4 deletions core/src/main/java/io/github/oasis/core/parser/GameParserYaml.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
import io.github.oasis.core.utils.Texts;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -44,6 +41,27 @@ public class GameParserYaml implements GameParseSupport {
private static final String ELEMENTS_KEY = "elements";
private static final String ELEMENT_PLUGIN_KEY = "plugin";

public static GameDef fromFile(File file) throws OasisParseException {
var fsContext = new ParserContext() {
@Override
public Object loadSiblingPath(String path) throws OasisParseException {
try {
return new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new OasisParseException("File not found in given path '" + path + "'!");
}


}
};

try (InputStream is = new FileInputStream(file)) {
return new GameParserYaml().parse(is, fsContext);
} catch (IOException e) {
throw new OasisParseException("Unable to parse game definition file in " + file.getAbsolutePath() + "!", e);
}
}

public static GameDef fromClasspath(String clzPathFile, ClassLoader clzLoader) throws OasisParseException {
var clzPathContext = new ParserContext() {
@Override
Expand Down
10 changes: 10 additions & 0 deletions engine/src/main/java/io/github/oasis/engine/DefinitionReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@

import io.github.oasis.core.EventJson;
import io.github.oasis.core.elements.AbstractRule;
import io.github.oasis.core.elements.GameDef;
import io.github.oasis.core.external.messages.GameCommand;
import io.github.oasis.core.external.messages.PersistedDef;
import io.github.oasis.core.external.messages.RuleCommand;
import io.github.oasis.engine.actors.cmds.EventMessage;
import io.github.oasis.engine.actors.cmds.Messages;
import io.github.oasis.engine.actors.cmds.OasisRuleMessage;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* @author Isuru Weerarathna
Expand All @@ -42,6 +45,13 @@ class DefinitionReader {
PersistedDef.GAME_RULE_UPDATED, RuleCommand.RuleChangeType.UPDATE
);

static List<AbstractRule> parseDefsToRules(int gameId, GameDef gameDef, EngineContext context) {
return gameDef.getRuleDefinitions()
.stream()
.map(def -> context.getParsers().parseToRule(def))
.collect(Collectors.toList());
}

static Object derive(PersistedDef def, EngineContext context) {
if (def.isEvent()) {
return new EventMessage(new EventJson(def.getData()), null, def.getMessageId());
Expand Down
60 changes: 59 additions & 1 deletion engine/src/main/java/io/github/oasis/engine/OasisEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,24 @@
import akka.actor.ActorSystem;
import akka.actor.Props;
import io.github.oasis.core.Event;
import io.github.oasis.core.elements.AbstractRule;
import io.github.oasis.core.elements.GameDef;
import io.github.oasis.core.exception.OasisException;
import io.github.oasis.core.external.EventStreamFactory;
import io.github.oasis.core.external.MessageReceiver;
import io.github.oasis.core.external.messages.GameCommand;
import io.github.oasis.core.external.messages.OasisCommand;
import io.github.oasis.core.external.messages.PersistedDef;
import io.github.oasis.engine.actors.ActorNames;
import io.github.oasis.engine.actors.OasisSupervisor;
import io.github.oasis.engine.actors.cmds.EventMessage;
import io.github.oasis.engine.actors.cmds.Messages;
import io.github.oasis.engine.ext.ExternalParty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
* @author Isuru Weerarathna
*/
Expand All @@ -45,7 +51,7 @@ public class OasisEngine implements MessageReceiver {
private ActorSystem oasisEngine;
private ActorRef supervisor;

private EngineContext context;
private final EngineContext context;

public OasisEngine(EngineContext context) {
this.context = context;
Expand Down Expand Up @@ -108,6 +114,58 @@ public void submitAll(Object... events) {
}
}

/**
* Creates a game in the engine. Basically this will prepare the game contexts and
* other internal things before starting the game.
* This game id must be available in engine database as well.
*
* @param gameId game id to create
*/
public OasisEngine createGame(int gameId) {
submit(GameCommand.create(gameId, GameCommand.GameLifecycle.CREATE));
return this;
}

/**
* Starts an already created game in this engine.
* It is mandatory to call {@link #createGame(int)} before calling this method.
*
* @param gameId game id to start
*/
public OasisEngine startGame(int gameId) {
submit(GameCommand.create(gameId, GameCommand.GameLifecycle.START));
return this;
}

/**
* Starts a game with provided element rules. All rules will be associated under the given game id.
*
* Once rules have been submitted, those will be returned as well.
*
* @param gameId game id to start
* @param gameDefWithRules parsed game definition with rules from file or resource.
* @return list of rules added to the game.
*/
public List<AbstractRule> startGame(int gameId, GameDef gameDefWithRules) {
submit(GameCommand.create(gameId, GameCommand.GameLifecycle.START));

List<AbstractRule> gameRules = DefinitionReader.parseDefsToRules(gameId, gameDefWithRules, context);
gameRules.stream()
.map(rule -> Messages.createRuleAddMessage(gameId, rule, null))
.forEach(this::submit);
return gameRules;
}

/**
* Stops the game with give id. Once this command is invoked, the engine will discard
* all the following events associated for this removed game.
*
* @param gameId game id to stop
*/
public void stopGame(int gameId) {
submit(GameCommand.create(gameId, GameCommand.GameLifecycle.REMOVE));
}

EngineContext getContext() {
return context;
}
Expand Down
24 changes: 8 additions & 16 deletions engine/src/test/java/io/github/oasis/engine/EngineBadgesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,9 @@
import io.github.oasis.core.elements.AttributeInfo;
import io.github.oasis.core.elements.GameDef;
import io.github.oasis.core.external.DbContext;
import io.github.oasis.core.external.messages.GameCommand;
import io.github.oasis.elements.badges.BadgeIDs;
import io.github.oasis.elements.badges.stats.BadgeStats;
import io.github.oasis.elements.badges.stats.to.GameRuleWiseBadgeLog;
import io.github.oasis.elements.badges.stats.to.GameRuleWiseBadgeLogRequest;
import io.github.oasis.elements.badges.stats.to.UserBadgeLog;
import io.github.oasis.elements.badges.stats.to.UserBadgeLogRequest;
import io.github.oasis.elements.badges.stats.to.UserBadgeRequest;
import io.github.oasis.elements.badges.stats.to.UserBadgeSummary;
import io.github.oasis.elements.badges.stats.to.*;
import io.github.oasis.engine.element.points.PointIDs;
import io.github.oasis.engine.model.TEvent;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -68,9 +62,9 @@ public void testEngineBadges() {

GameDef gameDef = loadRulesFromResource("rules/badges-basic.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
List<AbstractRule> rules = submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);
addRulesToMetadata(TEvent.GAME_ID, rules);

engine.submitAll(e1, e2, e3, e4, e5, e6);
awaitTerminated();

Expand Down Expand Up @@ -147,9 +141,8 @@ public void testEngineBadgesWithPoints() {

GameDef gameDef = loadRulesFromResource("rules/badges-points.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
List<AbstractRule> rules = submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5, e6);
awaitTerminated();

Expand Down Expand Up @@ -242,9 +235,8 @@ public void testEngineBadgesWithPointsRemoval() {

GameDef gameDef = loadRulesFromResource("rules/badges-removal-points.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
List<AbstractRule> rules = submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5);
awaitTerminated();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
package io.github.oasis.engine;

import io.github.oasis.core.Event;
import io.github.oasis.core.elements.AbstractRule;
import io.github.oasis.core.elements.GameDef;
import io.github.oasis.core.external.DbContext;
import io.github.oasis.core.external.messages.GameCommand;
import io.github.oasis.elements.challenges.ChallengeIDs;
import io.github.oasis.elements.challenges.ChallengeOverEvent;
import io.github.oasis.elements.challenges.stats.ChallengeStats;
Expand All @@ -35,11 +35,10 @@
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

import static io.github.oasis.engine.RedisAssert.assertKeyNotExist;
import static io.github.oasis.engine.RedisAssert.assertSorted;
import static io.github.oasis.engine.RedisAssert.ofSortedEntries;
import static io.github.oasis.engine.RedisAssert.*;

/**
* @author Isuru Weerarathna
Expand All @@ -57,9 +56,9 @@ public void testChallenges() {

GameDef gameDef = loadRulesFromResource("rules/challenges-basic.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);
addRulesToMetadata(TEvent.GAME_ID, rules);

engine.submitAll(e1, e2, e3, e4, e5, e6);
awaitTerminated();

Expand Down Expand Up @@ -178,9 +177,9 @@ public void testOutOfOrderChallenge() {
GameDef gameDef = loadRulesFromResource("rules/challenges-outoforder.yml");
String ruleId = "CHG000001";

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);
addRulesToMetadata(TEvent.GAME_ID, rules);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8,
ChallengeOverEvent.createFor(e1.getGameId(), ruleId));
awaitTerminated();
Expand Down Expand Up @@ -227,9 +226,8 @@ public void testOutOfOrderChallengeNoPointsUntil() {

GameDef gameDef = loadRulesFromResource("rules/challenges-outoforder.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
submitRules(engine, TEvent.GAME_ID, gameDef);
engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8);
awaitTerminated();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import io.github.oasis.core.Event;
import io.github.oasis.core.elements.AbstractRule;
import io.github.oasis.core.elements.GameDef;
import io.github.oasis.core.external.messages.GameCommand;
import io.github.oasis.elements.milestones.MilestoneIDs;
import io.github.oasis.elements.milestones.stats.MilestoneStats;
import io.github.oasis.elements.milestones.stats.to.GameMilestoneRequest;
Expand Down Expand Up @@ -52,9 +51,9 @@ public void testMilestones() {

GameDef gameDef = loadRulesFromResource("rules/milestone-basic.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);
addRulesToMetadata(TEvent.GAME_ID, rules);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8);
awaitTerminated();

Expand Down Expand Up @@ -95,9 +94,8 @@ public void testMilestoneMultiUsers() {

GameDef gameDef = loadRulesFromResource("rules/milestone-basic.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
submitRules(engine, TEvent.GAME_ID, gameDef);
engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8);
awaitTerminated();

Expand All @@ -121,9 +119,8 @@ public void testMilestonesPenalties() {

GameDef gameDef = loadRulesFromResource("rules/milestones-penalties.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
List<AbstractRule> rules = submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8);
awaitTerminated();

Expand Down Expand Up @@ -155,9 +152,8 @@ public void testMilestonesFromPoints() {

GameDef gameDef = loadRulesFromResource("rules/milestones-from-points.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
List<AbstractRule> rules = submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8);
awaitTerminated();

Expand Down Expand Up @@ -188,9 +184,8 @@ public void testMilestonesByCount() {

GameDef gameDef = loadRulesFromResource("rules/milestones-by-count.yml");

engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.CREATE));
engine.submit(GameCommand.create(TEvent.GAME_ID, GameCommand.GameLifecycle.START));
List<AbstractRule> rules = submitRules(engine, TEvent.GAME_ID, gameDef);
List<AbstractRule> rules = engine.createGame(TEvent.GAME_ID).startGame(TEvent.GAME_ID, gameDef);

engine.submitAll(e1, e2, e3, e4, e5, e6, e7, e8);
awaitTerminated();

Expand Down
Loading

0 comments on commit 0f5cced

Please sign in to comment.