Git Product home page Git Product logo

openliberty / liberty-bikes Goto Github PK

View Code? Open in Web Editor NEW
37.0 5.0 38.0 4.65 MB

Real-time web based multiplayer game running on OpenLiberty

Home Page: http://libertybikes.mybluemix.net

License: Eclipse Public License 1.0

Java 54.79% JavaScript 0.62% TypeScript 33.73% HTML 4.09% Shell 1.27% Dockerfile 0.46% SCSS 5.04%
open-liberty microprofile microservices microservices-demo jakartaee game hacktoberfest

liberty-bikes's Introduction

Liberty Bikes

Build Status

Image of Liberty Bikes game

Publicly hosted on IBM Cloud here: http://libertybikes.mybluemix.net/

Bluemix toolchain automatically deploys the current liberty-bikes/liberty-bikes:master branch

How to setup locally

Prereqs:

  • Java 8 or newer. Java must also be on the $PATH. If you can run java -version from a terminal window, then Java is on your $PATH.
  • Have Git installed
  • [Optional] Have Docker installed if you want to use the real database or Grafana dashboard.

Clone and run

First, clone this github repo with the following commands:

git clone [email protected]:OpenLiberty/liberty-bikes.git
cd liberty-bikes

If you have a Github account, press the "Fork" button in the top right corner of this web page to fork the repository.

Next, build and deploy all microservice applications on locally running liberty servers, then open the game in a web browser. If you are on Windows, you may need to manually open the game in a web browser at http://localhost:12000

./gradlew start frontend:open

Any code changes that are made in an IDE with auto-build enabled will automatically publish content to the loose application, meaning no server restarts should be required between code changes.

Optional Docker steps

By default, the player-service stores player registration and stats in-memory. To use a real database, you can start a PostgreSQL docker container with this script:

./startDB.sh

To start the monitoring services, you must have Docker installed. They can be started with:

./startMonitoring.sh

How to shut everything down cleanly

To stop all liberty servers, issue the command:

./gradlew stop

Run it locally in containers

(Requires docker and docker-compose to be installed. The Docker daemon must be running.)

Normally you get better performance running services outside of containers (aka bare metal), but if you want to build and run all of the containers locally, run the command:

./gradlew dockerStart

To stop and remove the containers, use:

./gradlew dockerStop

Technologies used

  • Java EE 8
    • CDI 2.0 (auth-service, game-service, player-service)
    • EE Concurrency (game-service, player-service)
    • JAX-RS 2.1 (auth-service, game-service, player-service)
    • JNDI (auth-service, game-service, player-service)
    • JSON-B (game-service, player-service)
    • WebSocket 1.1 (game-service)
  • MicroProfile 2.2
    • Config (auth-service, game-service, player-service)
    • JWT (auth-service, game-service, player-service)
    • Rest Client (game-service)
    • OpenAPI (auth-service, game-service, player-service)
    • Metrics (auth-service, game-service, player-service, frontend)
  • Angular 7 (frontend)
  • Prometheus for metric collection
  • Grafana for metric visualization
  • Gradle build
  • IBM Cloud Continuous Delivery Pipeline

JSON-B

Several of the backend entities need to be represtented as JSON data so they can be sent to the frontend via websocket, these include objects like GameBoard, Obstacle, and Player. Using POJOs and the occasional @JsonbTransient annotation, we used JSON-B to transform Java objects to JSON data.

public class GameBoard {

    @JsonbTransient
    public final short[][] board = new short[BOARD_SIZE][BOARD_SIZE];

    public final Set<Obstacle> obstacles = new HashSet<>();
    public final Set<MovingObstacle> movingObstacles = new HashSet<>();
    public final Set<Player> players = new HashSet<>();

    // ...
}

By default, JSON-B will expose any public members as well as public getXXX(), this includes other objects such as the Set<Player> players field. The resulting class gets serialized into something like this:

{
  "movingObstacles" : [ 
    { "height":12, "width":11, "x":13, "y":14 }
  ],
  "obstacles" : [
    { "height":2, "width":1, "x":3, "y":4 }
  ],
  "players" : [    
    { "id":"1234", "name":"Bob", "color":"#f28415", "status":"Connected", "alive":true, "x":9, "y":9, "width":3, "height":3, "direction":"RIGHT" }
  ]
}

MicroProfile Rest Client

Each of the 3 backend microservices in Liberty Bikes (auth, game, and player) exposed a REST API. In most cases the frontend would call the backend REST services, but sometimes the backend services had to call each other.

For example, when a game is over, the game service makes REST calls to the player service to update the player statistics. To accomplish this, the game-service simply defines a POJI (plain old Java Interface) that represents the player-service API it cares about, including the data model:

import javax.ws.rs.*;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@RegisterRestClient
@Path("/")
public interface PlayerService {

    @GET
    @Path("/player/{playerId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Player getPlayerById(@PathParam("playerId") String id);

    @POST
    @Path("/rank/{playerId}/recordGame")
    public void recordGame(@PathParam("playerId") String id, @QueryParam("place") int place);

}

public class Player {
    public String id;
    public String name;
}

Then, to use the Rest Client in the game service, we simply inject the interface and an implementation is proxied for us:

@ServerEndpoint("/round/ws/{roundId}")
public class GameRoundWebsocket {

    @Inject
    @RestClient
    PlayerService playerSvc;

    @Inject
    GameRoundService gameSvc;
    
    private final static Jsonb jsonb = JsonbBuilder.create();
    
    @OnMessage
    public void onMessage(@PathParam("roundId") final String roundId, String message, Session session) {
        InboundMessage msg = jsonb.fromJson(message, InboundMessage.class);
        GameRound round = gameSvc.getRound(roundId);
        // ...
        Player playerResponse = playerSvc.getPlayerById(msg.playerJoinedId);
        round.addPlayer(session, msg.playerJoinedId, playerResponse.name, msg.hasGameBoard);
        // ...
    }
}      

The only non-Java part about MP Rest Client is the need to specify the base path to the service via JVM option. This is easy enough to do in the build scripting, and easily overridable for cloud environments:

liberty {
  server {
    name = 'game-service'
    jvmOptions = ['-Dorg.libertybikes.restclient.PlayerService/mp-rest/url=http://localhost:8081/']
  }
}

Microprofile OpenAPI

Especially while developing new Rest APIs locally, it is useful to inspect the exposed APIs and test them out manually. Simply by enabling the mpOpenAPI-1.0 feature in server.xml (no application changes needed), all JAX-RS endpoints will be exposed in an interactive web UI.

Here is a snapshot of what the player-service view looks like:

Image of MP OpenAPI web ui

EE Concurrency

Executors from Java SE are very easy to use, and the "Managed" equivalent Executors in EE Concurrency lets you use all of the SE functionality with the added benefit of running the work on threads that are A) managed by the application server and B) have the proper thread context metadata to perform "EE type" operations such as CDI injections and JNDI lookups.

System.out.println("Scheduling round id=" + roundId + " for deletion in 5 minutes");
exec.schedule(() -> {
    allRounds.remove(roundId);
    System.out.println("Deleted round id=" + roundId);
}, 5, TimeUnit.MINUTES);

Liberty Gradle Plugin

Liberty Bikes can be built and run with a single command and no prereqs thanks to Gradle and the Liberty Gradle Plugin! With these build tools we can easily control a bunch of things:

  • Downloading and "installing" Liberty
  • Managing build and runtime dependencies (i.e. compile-time classpath and jars that get packaged inside the WAR applications)
  • Starting and stopping one or more Liberty servers

To get the Liberty gradle plugin, we add this dependency:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'net.wasdev.wlp.gradle.plugins:liberty-gradle-plugin:2.6.5'
  }
}

To control the Liberty distribution, we simply specify a dependency:

dependencies {
    libertyRuntime group: 'io.openliberty', name: 'openliberty-runtime', version: '[19.0.0.5,)'
}

Or, if we want to use a Beta image instead of an official GA'd image, we specify a URL in the liberty.install task instead of as a runtime dependency:

liberty {
  install {
    runtimeUrl = "https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/wasdev/downloads/wlp/beta/wlp-beta-2018.5.0.0.zip"
  }
}

Monitoring

If you run Liberty Bikes in a container environment using ./gradlew dockerStart, a Prometheus and Grafana instance will be started and preconfigured for monitoring the 4 Liberty Bikes microservices.

If you are running locally, you can open a browser to http://localhost:3000 and login with the username/password of admin/admin (respectively). The dashboard looks something like this:

Image of Grafana dashboard

The above shapshot shows basic data such as:

  • Service Health: Green/Red boxes for up/down respectively
  • System info: CPU load and memory usage
  • Current stats:
    • Number of players in queue
    • Number of players playing a game
    • Total actions/sec of players
  • Overall stats:
    • Total number of logins
    • Total number of games played

Any application-specific stats can be collected using MicroProfile Metrics. For example, to collect number of player logins, we added the following code to our createPlayer method:

    @Inject
    private MetricRegistry registry;

     private static final Metadata numLoginsCounter = new Metadata("num_player_logins", // name
                    "Number of Total Logins", // display name
                    "How many times a user has logged in.", // description
                    MetricType.COUNTER, // type
                    MetricUnits.NONE); // units

    @POST
    @Produces(MediaType.TEXT_HTML)
    public String createPlayer(@QueryParam("name") String name, @QueryParam("id") String id) {
      // ...
      registry.counter(numLoginsCounter).inc();
      // ...
    }

Continuous Delivery

Early on we set up a build pipeline on IBM Cloud that we pointed at this GitHub repository. Every time a new commit is merged into the master branch, the pipeline kicks off a new build and redeploys all of the services. The average time from pressing merge on a PR to having the changes live on libertybikes.mybluemix.net is around 20 minutes.

The pipeline UI looks like this in our dashboard:

Image of build pipeline

The pipeline consists of 2 stages: Build and Deploy.

The build stage simply points at the GitHub repository URL, and has a little bit of shell scripting where we define how to build the repo:

#!/bin/bash
export JAVA_HOME=~/java8
./gradlew clean build libertyPackage -Denv_mode=prod

For the deployment stage, each microservice gets its own step in the stage. We could also split the microservices into separate stages (or even different pipelines) if we didn't always want to redeploy all microservices. Like the build stage, the deploy stage has a little bit of shell scripting at each step:

#!/bin/bash

# Unzip the archive we receive as build input
cd game-service/build/libs
unzip game-service.zip -d game-service

# Set some Cloud Foundry env vars (use the latest WAS Liberty beta)
cf set-env "${CF_APP}" IBM_LIBERTY_BETA true
cf set-env "${CF_APP}" JBP_CONFIG_LIBERTY "version: +"

# Override the player-service URL for MP Rest Client on game-service
echo "-Dorg.libertybikes.restclient.PlayerService/mp-rest/url=\
http://player-service.mybluemix.net/" > game-service/wlp/usr/servers/game-service/jvm.options

# Push the entire server directory into Cloud Foundry
cf push "${CF_APP}" -p "game-service/wlp/usr/servers/game-service"

Originally cloned from https://github.com/aguibert/coms319-project4

liberty-bikes's People

Contributors

aguibert avatar maihameed avatar olendvcook avatar realmodusoperandi avatar ryanesch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

liberty-bikes's Issues

Pluggable AI: Security Concerns

Investigate if we have any security concerns with having pluggable ai such as denial of service attacks or sending huge json objects through websocket.

The actual Pluggable AI service will be ran on each lab attendees computer which alleviates most concerns.

Change Start Button to Start / Pause Toggle button

Feedback from All Things Open: We had a hard time getting multiple players into the game at the same time given the lag of typing / entering the game. Thought it might be nice to change the Start button to be a toggle button with Start / Pause. This way you could pause the game timer and let 4 people get into the game at the same time then continue.

Initial Drop of Pluggable AI

Initial drop of creating a new service that spawns a separate server that runs skeleton code that connects to the game service and joins as an external ai player

Pluggable AI

Instead of users directly controlling a single player, they could write a simple microservice that receives the game board and decides what direction to turn at each game tick. This could be implemented where each player stands up an OpenLiberty server with an app implementing our AI interface. Participants would also need a way to register their AI with the host service.

This would allow Liberty Bikes to become a hands-on lab, and at the end of the lab everyone could register their AI and compete against each other for a prize at the end.

This would also make a great guide on openliberty.io.

The Bot-Players are too good!

When there are only one or two human players, chances are that one of the bots will usually win. This might be partly due to the lag introduced by using a mobile phone as a controller.

FFDC: com.ibm.ws.container.service.state.StateChangeException

Seeing this FFDC quite often, not sure if its because i'm just running gradlew start frontend:open over and over

Stack Dump = com.ibm.ws.container.service.state.StateChangeException: java.util.concurrent.RejectedExecutionException: CWWKE1202E: A task cannot be submitted because the executor managedExecutorService[DefaultManagedExecutorService] (concurrencyPolicy[defaultConcurrencyPolicy]) has been shut down.
	at com.ibm.ws.container.service.state.internal.ApplicationStateManager.fireStarting(ApplicationStateManager.java:32)
	at com.ibm.ws.container.service.state.internal.StateChangeServiceImpl.fireApplicationStarting(StateChangeServiceImpl.java:50)
	at com.ibm.ws.app.manager.module.internal.SimpleDeployedAppInfoBase.preDeployApp(SimpleDeployedAppInfoBase.java:550)
	at com.ibm.ws.app.manager.module.internal.SimpleDeployedAppInfoBase.installApp(SimpleDeployedAppInfoBase.java:511)
	at com.ibm.ws.app.manager.module.internal.DeployedAppInfoBase.deployApp(DeployedAppInfoBase.java:347)
	at com.ibm.ws.app.manager.war.internal.WARApplicationHandlerImpl.install(WARApplicationHandlerImpl.java:65)
	at com.ibm.ws.app.manager.internal.statemachine.StartAction.execute(StartAction.java:140)
	at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.enterState(ApplicationStateMachineImpl.java:1261)
	at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.performAction(ApplicationStateMachineImpl.java:1129)
	at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.run(ApplicationStateMachineImpl.java:884)
	at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:239)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:825)
Caused by: java.util.concurrent.RejectedExecutionException: CWWKE1202E: A task cannot be submitted because the executor managedExecutorService[DefaultManagedExecutorService] (concurrencyPolicy[defaultConcurrencyPolicy]) has been shut down.
	at com.ibm.ws.threading.internal.PolicyExecutorImpl.enqueue(PolicyExecutorImpl.java:521)
	at com.ibm.ws.threading.internal.PolicyExecutorImpl.invokeAll(PolicyExecutorImpl.java:655)
	at com.ibm.ws.concurrent.internal.ManagedExecutorServiceImpl.invokeAll(ManagedExecutorServiceImpl.java:413)
	at org.jboss.weld.executor.AbstractExecutorServices.invokeAllAndCheckForExceptions(AbstractExecutorServices.java:59)
	at org.jboss.weld.executor.AbstractExecutorServices.invokeAllAndCheckForExceptions(AbstractExecutorServices.java:67)
	at org.jboss.weld.bootstrap.ConcurrentBeanDeployer.addClasses(ConcurrentBeanDeployer.java:52)
	at org.jboss.weld.bootstrap.BeanDeployment.createClasses(BeanDeployment.java:198)
	at org.jboss.weld.bootstrap.WeldStartup.startInitialization(WeldStartup.java:412)
	at org.jboss.weld.bootstrap.WeldBootstrap.startInitialization(WeldBootstrap.java:79)
	at com.ibm.ws.cdi.impl.CDIContainerImpl$2.run(CDIContainerImpl.java:144)
	at com.ibm.ws.cdi.impl.CDIContainerImpl$2.run(CDIContainerImpl.java:141)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:647)
	at com.ibm.ws.cdi.impl.CDIContainerImpl.startInitialization(CDIContainerImpl.java:141)
	at com.ibm.ws.cdi.liberty.CDIRuntimeImpl.applicationStarting(CDIRuntimeImpl.java:443)
	at com.ibm.ws.container.service.state.internal.ApplicationStateManager.fireStarting(ApplicationStateManager.java:28)
	... 13 more

Add links, etc inside game for guides, source, etc

So typically when your talking about Open Liberty and this game at a conference, you may want to reference stuff like the following which is all outside of the game:

  1. openliberty.io -> guides, etc.
  2. liberty-bikes source page
  3. github/OpenLiberty
  4. A deck of slides that talks about Open Liberty and what it is, etc.

Would be nice to have all this built into the game since it can be difficult to toggle between the demo browser and other sites. Similar to how standup arcade games did in the 80's with a demo, and the credits for who wrote the game, or the how to play the game, etc.
Have this information be part of the "demo-mode" when the game is not being played(game play -> what is Open Liberty -> Source -> Guides -> game play). Also have a button to go directly to this information for the people doing the demo so they can talk to it if the game is not on the screen they want to discuss.

Remove party code for conferences/events

When using the app at an event/conference, the same party code tends to not change. If there are machines setup to play locally (since using a phone would take longer to open a browser, navigate to the site, and hope the internet at the conference is fast enough), users should be able to quickly join the hosted game without needing to type in the code.
Two possible approaches are:

  1. Users should be able to remain on the game page and just change the username
  2. Eliminate the need for users to type in the party code on the current login page (either by removing the code entirely, or populating the field with the last-used code.

W/e the solution, the goal is to streamline users coming and going when using dedicated machines at events; for the end user, they only care that they can see their name to help identify their bike as well as for the leadboard.

Downgrade the quality of the Hal and Wally AI bots once all humans have been eliminated from the game

When playing at conferences, often a player may be eliminated quickly by not understanding the controls, etc, and then the remaining AI bots for Hal and Wally often keep playing for up to 60 seconds.

Would like a simple change such that once all the human players are eliminated from a round that the two AI bots automatically just go in a straight line for the remainder of the time they are alive (ie, we want to eliminate the AI bot players as fast as possible so the game can quickly be restarted for another round).

Make single-party mode on by default

In most settings (local or at a conference) single party mode is the more common mode to use. Multi party mode is only necessary for cloud deployments

Party Host getting kicked to login page because round already finished.

If a player joins a round before the host instance does (like if he's in queue it will requeue immediately while the host session waits 5 seconds to auto queue) and then the 1 player leaves the game, we will close the game for having no players. After the host session moves on to the next round they get an alert that the round is over and are kicked to the login screen.

Copyright and license statements

I'm assuming we're going to have to add them to every source file. Do we want to get a head start on that now or wait till the end and then sed em all in at once?

(Guest) Login hits PlayerService.CreatePlayer twice

not sure if it's just for Guest login:

[5/28/19, 11:38:02:948 CDT] 00000a43 SystemOut                                                    O Created a new player with id=BASIC:Olen
[5/28/19, 11:38:02:951 CDT] 00000b10 SystemOut                                                    O A player already existed with id=BASIC:Olen

hitting the login button on the frontend hits org.libertybikes.player.service.PlayerService.createPlayer(String, String) twice.

No 4 digit code when playing a locally hosted version

Another item we have noticed when demoing the game at conferences, is that having to type in the 4 digit round code is cumbersome. Its particularly hard to do when your trying to engage with the attendees and hurry to get them into a round of the game.

Secondly, once you play around, the UI on the mobile device bumps you back out where you have to re-enter the code after each game played. Would be nice to have it where you didnt bump the user out of the game, but auto queued them for the next round since we typically just hand the device to the next person that walks up at the conference.

This is somewhat tied to #171 as well for ease of usage.

Handle having Pluggable AI registration be unique

We have to figure out a way to have each pluggable ai registration be unique, because someone could just currently copy a different bots name and purposely play bad to tank their ranking.

Current idea is to send the pluggable ai service some kind of token when they first register that they can then store and use to connect with later

After the AI is logged in through the frontend the game service should ask the ai for it's token.

Can't join game when hosting a large group

Hello, I was recently showcasing Liberty Bikes as part of a Microservices workshop with 12 attendees. When asked to join the game as guests, a couple of attendees couldn't join.

Details:

  • Pressing the SIGN IN AS GUEST button after inputting their username has the 'button clicked' feedback but wouldn't take them into the game.
  • Asking the user to use a different web browser or their phone wouldn't work either.

Just wanted to point this out in case you wanted to look into it for future large group workshops :). Cheers.

It isn't clear that the edges will kill you too

Either marking the edges in red, the same as other obstacles
OR
instead of crashing, make the edges wrap around such that if you leave the screen on one side, you re-enter on the other?

StackOverflowException when hosting single party round for a long time

When demoing at EclipseCon EU 2018, the demo would run fine in single party mode for a few hours and then hang. Turns out it was due to the following SOE:

Stack Dump = org.apache.cxf.interceptor.Fault
	at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:162)
	at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:128)
	at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.invoke(LibertyJaxRsInvoker.java:273)
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:193)
	at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.invoke(LibertyJaxRsInvoker.java:444)
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:103)
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:88)
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:116)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:124)
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:274)
	at com.ibm.ws.jaxrs20.endpoint.AbstractJaxRsWebEndpoint.invoke(AbstractJaxRsWebEndpoint.java:134)
	at com.ibm.websphere.jaxrs.server.IBMRestServlet.handleRequest(IBMRestServlet.java:146)
	at com.ibm.websphere.jaxrs.server.IBMRestServlet.doGet(IBMRestServlet.java:112)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
	at com.ibm.websphere.jaxrs.server.IBMRestServlet.service(IBMRestServlet.java:96)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1255)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:743)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:440)
	at com.ibm.ws.webcontainer.filter.WebAppFilterChain.invokeTarget(WebAppFilterChain.java:182)
	at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:93)
	at com.ibm.ws.security.jaspi.JaspiServletFilter.doFilter(JaspiServletFilter.java:56)
	at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:201)
	at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90)
	at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:996)
	at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1134)
	at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:4954)
	at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.handleRequest(DynamicVirtualHost.java:314)
	at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:996)
	at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:279)
	at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:1011)
	at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.wrapHandlerAndExecute(HttpDispatcherLink.java:414)
	at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:373)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:532)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:466)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:331)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.ready(HttpInboundLink.java:302)
	at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:165)
	at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:74)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager.requestComplete(WorkQueueManager.java:501)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager.attemptIO(WorkQueueManager.java:571)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager.workerRun(WorkQueueManager.java:926)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager$Worker.run(WorkQueueManager.java:1015)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.StackOverflowError
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.checkServiceClass(ServiceRegistry.java:1106)
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:238)
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.getService(ServiceFactoryUse.java:111)
	at org.eclipse.osgi.internal.serviceregistry.ServiceConsumer$2.getService(ServiceConsumer.java:45)
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.getService(ServiceRegistrationImpl.java:508)
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.getService(ServiceRegistry.java:461)
	at org.eclipse.osgi.internal.framework.BundleContextImpl.getService(BundleContextImpl.java:624)
	at org.apache.aries.jndi.Utils$4.run(Utils.java:195)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.apache.aries.jndi.Utils.getServicePrivileged(Utils.java:193)
	at org.apache.aries.jndi.ContextHelper.getInitialContextUsingBuilder(ContextHelper.java:231)
	at org.apache.aries.jndi.ContextHelper.getContextProvider(ContextHelper.java:163)
	at org.apache.aries.jndi.ContextHelper.getInitialContext(ContextHelper.java:141)
	at org.apache.aries.jndi.OSGiInitialContextFactoryBuilder.getInitialContext(OSGiInitialContextFactoryBuilder.java:51)
	at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
	at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
	at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:341)
	at javax.naming.InitialContext.lookup(InitialContext.java:417)
	at javax.naming.InitialContext.doLookup(InitialContext.java:290)
	at org.libertybikes.game.maps.GameMap.create(GameMap.java:25)
	at org.libertybikes.game.maps.GameMap.create(GameMap.java:35)
	at org.libertybikes.game.core.GameBoard.initializeGameMap(GameBoard.java:54)
	at org.libertybikes.game.core.GameBoard.<init>(GameBoard.java:50)
	at org.libertybikes.game.core.GameBoard.<init>(GameBoard.java:44)
	at org.libertybikes.game.core.GameRound.<init>(GameRound.java:66)
	at org.libertybikes.game.round.service.GameRoundService.createRoundById(GameRoundService.java:51)
	at org.libertybikes.game.round.service.GameRoundService.requeue(GameRoundService.java:90)
	at org.libertybikes.game.round.service.GameRoundService.requeue(GameRoundService.java:97)
	at org.libertybikes.game.round.service.GameRoundService.requeue(GameRoundService.java:97)
	at org.libertybikes.game.round.service.GameRoundService.requeue(GameRoundService.java:97)
       <requeue repeats a few hundred more times>

liberty-bikes-logs.zip

Conference optimizations

Do a full test-run of a conference scenario:

  • mobile phones
  • BYO router
  • connecting to external display

Take note of pain points in the setup process, record bugs, bring in others to test UX.

Replace the alerts() with in-lined notification

Browser alerts are used on the 'login' page, however, they provide a poor UX (forcing user to scroll all the way to the top dismiss an alert). Instead, the alert should appear below the box for which it is intended for, similarly to not meeting correct characters for email criteria, etc.

Also, when hitting the ENTER key in the 'Party Code' input field, it should trigger a click of the 'Join Party' button.

Error when more than 4 users try to connect to a game

When demo-ing this at the Festival of Innovation in Hursley we came across an issue where the countdown clock to a game would negative numbers if more than 4 players tried to connect to a game. Once we dropped this number and prevented more than 4 players from trying to play a single game at a time, the countdown reset from 3 and the game started at 0 (as expected)

Dashboard showing Health, Metrics and Open Tracing

Create a dashboard showing Health, Metrics and Open Tracing.

Sebastian Dashner has created a Grafana dashboard for Liberty and has a demo of running Prometheus/Grafana in Docker which might be a good starting point.

Accessibility: Enable WASD keys for control

The WASD keys (commonly used to control characters in games) should be an option for controlling the bikes, along with the arrow keys. In conferences, it's likely that laptops will be used which sometimes (especially macs) tend to have poor or terrible arrow keys, many times being half-size. This makes it very difficult for some users to play.

Frontend should trim party code

If someone enters extra whitespace on the party code input, we should trim the whitespace for when the backend checks it.

Show service health on main game page

Display some green/red icons representing MP health check result on the game page.

If it's determined that this information would be more appropriately displayed on the admin/status page, we can close this issue.

Docker / Kube version of liberty-bikes

A suggestion was to have a docker / kube environment version of the game with two versions of liberty-bikes. Show version A of the game, then have another version B with some easily distinguishable change that could be deployed to the container as part of the demo.

Pluggable AI Registration

On the login page, we will probably have a new button to register ai.
The form will take in the ip and port of the server running the pluggable ai.

it will then either redirect to the page that regular players see after they log in (page asking for room code or play now etc) or auto connect if Single Party Mode is on (which might send it to the queue)

Handle Re-qeuing for Pluggable AI

Figure out how to handle re-queuing for Pluggable AI.

Should they re-queue automatically?
Should the AI have to send a websocket message to re-queue or hit and endpoint?

Handle iOS 13 iPads correctly

Starting in the next release of iOS, iPads will identify themselves as macOS devices to websites in order to get the desktop version of webpages. However, we really do need to send it the mobile page, because the desktop version can only be played with a keyboard, and the on screen keyboard does not have arrow keys.

Possible solutions (easy to hard):

  • Document somehow that iPad users must request the mobile site to play
  • Figure out how to really detect an iPad and send them to the controls page
  • Add the ability to show the touch controls on the desktop game screen

IMG_0286

Add block diagram with application to readme information

What: When talking about Liberty Bikes at conferences, we often reference the readme after playing the game. Would be nice to have the block diagram of the 3 services, ui, and database with the different features of Java EE8 and MicroProfile that are used.

Add Metrics

Add MicroProfile Metrics to the services. This involves enabling metrics for the JVM and Liberty, but also instrumenting the services for application metrics. We'll need to decide what metrics would be interesting from the services. E.g. timings, number of users / games, etc.

Optionally use a DB for storing player ranking information

Currently the player-service stores all ranking information simply in-memory. This is simple, but it's not ideal for conference/lab scenarios where the ranking information matters enough that a player-service crash or unintentional restart would be bad.

Add Open Tracing

Add Open Tracing support. By default all JAX-RS methods are traced and that may be sufficient, in which case we just need to enable Open Tracing in the runtimes.

Add Health checks

Add MicroProfile Health checks to each service. This will need to consider what it means for each service to be healthy, which might be influenced by the availability of the services they depend on.

dockerStart fails after clean due to missing Postgres jar

Performing the docker-compose start command (dockerStart and/or composeUp) after clean` (but before running the standard start command) causes the compose up step to fail building the player-service image:

Step 3/6 : ADD --chown=1001:0 build/libs/postgresql-*.jar /config/postgresql
ADD failed: no source files were specified

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.