Compare commits

..

No commits in common. "0fcd12d8857904068859a37a96b5cafc65ccbc02" and "5d769c39899eb54b85aff6c88c5cda8f6875fe0f" have entirely different histories.

50 changed files with 259 additions and 320 deletions

View File

@ -1,11 +1,11 @@
image: debian/unstable image: ubuntu/lts
packages: packages:
- openjdk-19-jdk - openjdk-11-jre-headless
- maven - maven
sources: sources:
- https://git.sr.ht/~crg/lingo - https://git.sr.ht/~crg/lingo
artifacts: artifacts:
- lingo/server/target/lingo.jar - lingo/server/target/lingo-server.jar
tasks: tasks:
- build: | - build: |
cd lingo cd lingo

View File

@ -1 +1 @@
web: java -jar server/target/lingo.jar web: java -jar server/target/lingo-server.jar

View File

@ -1,19 +1,17 @@
## Lingo ## Lingo
[![builds.sr.ht status](https://builds.sr.ht/~crg.svg)](https://builds.sr.ht/~crg?)
A word guessing game based on the [game show](https://en.wikipedia.org/wiki/Lingo_(American_game_show)). A word guessing game based on the [game show](https://en.wikipedia.org/wiki/Lingo_(American_game_show)).
[Screenshots](screenshots/) [Screenshots](screenshots/)
#### Browser client #### Browser client
- Practice: https://lingo.gould.dev/practice.html - Practice: https://lingo.charego.com/practice.html
- Competitive: https://lingo.gould.dev - Competitive: https://lingo.charego.com
#### JavaFX client #### JavaFX client
- Requirements: Java 19, Maven 3 - Requirements: Java 11, Maven 3
- Build: `mvn clean install` - Build: `mvn clean install`
- Start client: `mvn -f client javafx:run` - Start client: `mvn -f client javafx:run`
@ -21,8 +19,8 @@ A word guessing game based on the [game show](https://en.wikipedia.org/wiki/Ling
- Build: `mvn clean install` - Build: `mvn clean install`
- Start server - Start server
* Default port (8080): `java -jar server/target/lingo.jar` * Default port (8080): `java -jar server/target/lingo-server.jar`
* Custom port: `java -jar server/target/lingo.jar --server.port=<port>` * Custom port: `java -jar server/target/lingo-server.jar --server.port=<port>`
- Start client - Start client
* Browser: `open http://localhost:<port>` * Browser: `open http://localhost:<port>`
* JavaFX: `mvn -f client javafx:run -Dlingo-url=http://localhost:<port>` * JavaFX: `mvn -f client javafx:run -Dlingo-url=http://localhost:<port>`

View File

@ -3,8 +3,8 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>dev.gould</groupId> <groupId>com.charego</groupId>
<artifactId>lingo</artifactId> <artifactId>lingo-parent</artifactId>
<version>1.0</version> <version>1.0</version>
</parent> </parent>

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.api; package com.charego.lingo.api;
public class Destinations { public class Destinations {

View File

@ -1,3 +1,3 @@
module dev.gould.lingo.api { module com.charego.lingo.api {
exports dev.gould.lingo.api; exports com.charego.lingo.api;
} }

View File

@ -3,8 +3,8 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>dev.gould</groupId> <groupId>com.charego</groupId>
<artifactId>lingo</artifactId> <artifactId>lingo-parent</artifactId>
<version>1.0</version> <version>1.0</version>
</parent> </parent>
@ -12,8 +12,8 @@
<name>Lingo :: Client</name> <name>Lingo :: Client</name>
<properties> <properties>
<javafx.version>19</javafx.version> <javafx.version>11.0.2</javafx.version>
<lingo.url>https://lingo.gould.dev</lingo.url> <lingo.url>https://lingo.charego.com</lingo.url>
</properties> </properties>
<dependencies> <dependencies>
@ -52,14 +52,22 @@
<plugin> <plugin>
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId> <artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version> <version>0.0.4</version>
<configuration> <configuration>
<mainClass>dev.gould.lingo.client/dev.gould.lingo.client.bootstrap.LingoClient</mainClass> <mainClass>com.charego.lingo.client.bootstrap.LingoClient</mainClass>
<options> <options>
<option>-Dlingo.url=${lingo.url}</option> <option>-Dlingo.url=${lingo.url}</option>
</options> </options>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>com.charego.lingo.client.bootstrap.LingoClient</mainClass>
<addResourcesToClasspath>true</addResourcesToClasspath>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.bootstrap; package com.charego.lingo.client.bootstrap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;

View File

@ -1,12 +1,12 @@
package dev.gould.lingo.client.bootstrap; package com.charego.lingo.client.bootstrap;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import dev.gould.lingo.client.multiplayer.MultiplayerConfig; import com.charego.lingo.client.multiplayer.MultiplayerConfig;
import dev.gould.lingo.client.multiplayer.MultiplayerPresenter; import com.charego.lingo.client.multiplayer.MultiplayerPresenter;
import dev.gould.lingo.client.singleplayer.SinglePlayerPresenter; import com.charego.lingo.client.singleplayer.SinglePlayerPresenter;
import dev.gould.lingo.client.util.FxmlController; import com.charego.lingo.client.util.FxmlController;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.bootstrap; package com.charego.lingo.client.bootstrap;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,9 +10,9 @@ import java.util.Set;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import dev.gould.lingo.common.WordReader; import com.charego.lingo.common.WordReader;
import jakarta.annotation.PostConstruct; import javax.annotation.PostConstruct;
@Component @Component
public class WordRepository { public class WordRepository {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.multiplayer; package com.charego.lingo.client.multiplayer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.multiplayer; package com.charego.lingo.client.multiplayer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
@ -6,9 +6,9 @@ import java.util.UUID;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer; import java.util.function.Consumer;
import jakarta.annotation.PostConstruct; import javax.annotation.PostConstruct;
import dev.gould.lingo.common.LobbyData; import com.charego.lingo.common.LobbyData;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -34,14 +34,14 @@ import javafx.scene.paint.Color;
import javafx.scene.text.Font; import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment; import javafx.scene.text.TextAlignment;
import javafx.scene.web.WebView; import javafx.scene.web.WebView;
import dev.gould.lingo.api.Destinations; import com.charego.lingo.api.Destinations;
import dev.gould.lingo.client.util.FxmlController; import com.charego.lingo.client.util.FxmlController;
import dev.gould.lingo.client.view.Board; import com.charego.lingo.client.view.Board;
import dev.gould.lingo.client.view.OpponentBoard; import com.charego.lingo.client.view.OpponentBoard;
import dev.gould.lingo.client.view.PlayerBoard; import com.charego.lingo.client.view.PlayerBoard;
import dev.gould.lingo.common.Game; import com.charego.lingo.common.Game;
import dev.gould.lingo.common.GameLeftMessage; import com.charego.lingo.common.GameLeftMessage;
import dev.gould.lingo.common.Report; import com.charego.lingo.common.Report;
@Component @Component
public class MultiplayerPresenter implements FxmlController { public class MultiplayerPresenter implements FxmlController {

View File

@ -1,11 +1,11 @@
package dev.gould.lingo.client.multiplayer; package com.charego.lingo.client.multiplayer;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer; import java.util.function.Consumer;
import jakarta.annotation.PostConstruct; import javax.annotation.PostConstruct;
import jakarta.annotation.PreDestroy; import javax.annotation.PreDestroy;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,8 +1,8 @@
package dev.gould.lingo.client.singleplayer; package com.charego.lingo.client.singleplayer;
import java.util.Arrays; import java.util.Arrays;
import dev.gould.lingo.client.bootstrap.WordRepository; import com.charego.lingo.client.bootstrap.WordRepository;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,10 +21,10 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.text.Font; import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment; import javafx.scene.text.TextAlignment;
import dev.gould.lingo.client.view.PlayerBoard; import com.charego.lingo.client.view.PlayerBoard;
import dev.gould.lingo.common.Game; import com.charego.lingo.common.Game;
import dev.gould.lingo.common.Player; import com.charego.lingo.common.Player;
import dev.gould.lingo.common.Report; import com.charego.lingo.common.Report;
public class SinglePlayerPresenter { public class SinglePlayerPresenter {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.util; package com.charego.lingo.client.util;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.view; package com.charego.lingo.client.view;
import javafx.scene.canvas.Canvas; import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext; import javafx.scene.canvas.GraphicsContext;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.view; package com.charego.lingo.client.view;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.view; package com.charego.lingo.client.view;
import javafx.scene.canvas.Canvas; import javafx.scene.canvas.Canvas;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.client.view; package com.charego.lingo.client.view;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;

View File

@ -1,7 +1,7 @@
module dev.gould.lingo.client { module com.charego.lingo.client {
requires dev.gould.lingo.common; requires com.charego.lingo.common;
requires dev.gould.lingo.api; requires com.charego.lingo.api;
requires jakarta.annotation; requires java.annotation;
requires javafx.controls; requires javafx.controls;
requires javafx.fxml; requires javafx.fxml;
requires javafx.graphics; requires javafx.graphics;
@ -15,8 +15,8 @@ module dev.gould.lingo.client {
requires spring.messaging; requires spring.messaging;
requires spring.web; requires spring.web;
requires spring.websocket; requires spring.websocket;
exports dev.gould.lingo.client.bootstrap; exports com.charego.lingo.client.bootstrap;
exports dev.gould.lingo.client.multiplayer; exports com.charego.lingo.client.multiplayer;
opens dev.gould.lingo.client.bootstrap to javafx.fxml, spring.core; opens com.charego.lingo.client.bootstrap to javafx.fxml, spring.core;
opens dev.gould.lingo.client.multiplayer to javafx.fxml, spring.core; opens com.charego.lingo.client.multiplayer to javafx.fxml, spring.core;
} }

View File

@ -1,2 +1,2 @@
# https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html # https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
logging.level.dev.gould.lingo = DEBUG logging.level.com.charego.lingo = DEBUG

View File

@ -8,7 +8,7 @@
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<BorderPane xmlns:fx="http://javafx.com/fxml" <BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="dev.gould.lingo.client.bootstrap.LingoPresenter" fx:controller="com.charego.lingo.client.bootstrap.LingoPresenter"
fx:id="content" fx:id="content"
prefWidth="650" prefWidth="650"
prefHeight="420"> prefHeight="420">

View File

@ -8,7 +8,7 @@
<?import javafx.scene.web.WebView?> <?import javafx.scene.web.WebView?>
<StackPane xmlns:fx="http://javafx.com/fxml" <StackPane xmlns:fx="http://javafx.com/fxml"
fx:controller="dev.gould.lingo.client.multiplayer.MultiplayerPresenter" fx:controller="com.charego.lingo.client.multiplayer.MultiplayerPresenter"
fx:id="contentPane"> fx:id="contentPane">
<children> <children>

View File

@ -3,8 +3,8 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>dev.gould</groupId> <groupId>com.charego</groupId>
<artifactId>lingo</artifactId> <artifactId>lingo-parent</artifactId>
<version>1.0</version> <version>1.0</version>
</parent> </parent>

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
public class ChatMessage { public class ChatMessage {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
public class GameLeftMessage { public class GameLeftMessage {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
import java.util.Collection; import java.util.Collection;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
public class Report { public class Report {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
public class SetUsernameMessage { public class SetUsernameMessage {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.common; package com.charego.lingo.common;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;

View File

@ -1,4 +1,4 @@
module dev.gould.lingo.common { module com.charego.lingo.common {
requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.annotation;
exports dev.gould.lingo.common; exports com.charego.lingo.common;
} }

View File

@ -5,11 +5,11 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version> <version>2.3.0.RELEASE</version>
</parent> </parent>
<groupId>dev.gould</groupId> <groupId>com.charego</groupId>
<artifactId>lingo</artifactId> <artifactId>lingo-parent</artifactId>
<version>1.0</version> <version>1.0</version>
<packaging>pom</packaging> <packaging>pom</packaging>
@ -17,7 +17,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>19</java.version> <java.version>11</java.version>
</properties> </properties>
<modules> <modules>

View File

@ -3,8 +3,8 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>dev.gould</groupId> <groupId>com.charego</groupId>
<artifactId>lingo</artifactId> <artifactId>lingo-parent</artifactId>
<version>1.0</version> <version>1.0</version>
</parent> </parent>
@ -17,46 +17,56 @@
<version>${project.version}</version> <version>${project.version}</version>
<artifactId>lingo-api</artifactId> <artifactId>lingo-api</artifactId>
</dependency> </dependency>
<!-- Web -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-websocket</artifactId>
</dependency> </dependency>
<!-- Development Tools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- Prevent transitive application to other modules -->
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>lingo</finalName>
<plugins> <plugins>
<!-- Repackage as executable JAR (java -jar) -->
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<!-- Enable hot refreshing of resources --> <!-- Enable hot refreshing of resources -->
<!-- Add src/main/resources to the classpath -->
<!-- Remove duplicate resources from target/classes -->
<addResources>true</addResources> <addResources>true</addResources>
<image> <finalName>${project.artifactId}</finalName>
<name>docker.io/charego/lingo</name>
</image>
</configuration> </configuration>
<executions>
<!-- Repackage as executable JAR (java -jar) -->
<execution>
<id>repackage</id>
</execution>
</executions>
</plugin> </plugin>
<!-- How to build image directly to Docker daemon: --> <!-- How to build image directly to Docker daemon: -->
<!-- $ mvn jib:dockerBuild --> <!-- $ mvn jib:dockerBuild -->
<!-- How to build image and publish to Docker registry: --> <!-- How to build image and publish to Docker registry: -->
<!-- $ mvn jib:build --> <!-- $ mvn jib:build -Djib.to.image=<myregistry>/<myimage>:latest -->
<plugin> <plugin>
<groupId>com.google.cloud.tools</groupId> <groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId> <artifactId>jib-maven-plugin</artifactId>
<version>3.3.1</version> <version>2.2.0</version>
<configuration> <configuration>
<!-- Enable this setting to allow pushes to local Docker registry --> <!-- Enable this setting to allow pushes to local Docker registry -->
<!--<allowInsecureRegistries>true</allowInsecureRegistries>--> <!--<allowInsecureRegistries>true</allowInsecureRegistries>-->
<from> <from>
<image>eclipse-temurin:19-jre</image> <image>adoptopenjdk:11-jre-openj9</image>
</from> </from>
<to>
<image>docker.io/charego/lingo</image>
</to>
<container> <container>
<mainClass>dev.gould.lingo.server.LingoServer</mainClass>
<ports> <ports>
<port>8080</port> <port>8080</port>
</ports> </ports>

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.server; package com.charego.lingo.server;
import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER; import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER;
@ -8,10 +8,10 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import jakarta.annotation.PostConstruct; import javax.annotation.PostConstruct;
import dev.gould.lingo.api.Destinations; import com.charego.lingo.api.Destinations;
import dev.gould.lingo.common.*; import com.charego.lingo.common.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -30,20 +30,20 @@ public class LingoController {
private static final Logger log = LoggerFactory.getLogger(LingoController.class); private static final Logger log = LoggerFactory.getLogger(LingoController.class);
private final SimpMessagingTemplate messagingTemplate; @Autowired
private final SessionManager sessionManager; private SimpMessagingTemplate messagingTemplate;
private final WordRepository wordRepo;
private final Map<Integer, Game> gameById = new TreeMap<>();
private final Map<Player, Game> gameByPlayer = new HashMap<>();
private final Set<String> usernames = new HashSet<>();
@Autowired @Autowired
public LingoController(SimpMessagingTemplate messagingTemplate, SessionManager sessionManager, WordRepository wordRepo) { private SessionManager sessionManager;
this.messagingTemplate = messagingTemplate;
this.sessionManager = sessionManager; @Autowired
this.wordRepo = wordRepo; private WordRepository wordRepo;
}
private final Map<Integer, Game> gameById = new TreeMap<>();
private final Map<Player, Game> gameByPlayer = new HashMap<>();
private final Set<String> usernames = new HashSet<>();
@MessageMapping("/chat") @MessageMapping("/chat")
public ChatMessage chat(String message, @Header(SESSION_ID_HEADER) String sessionId) { public ChatMessage chat(String message, @Header(SESSION_ID_HEADER) String sessionId) {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.server; package com.charego.lingo.server;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.server; package com.charego.lingo.server;
import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER; import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER;
@ -16,28 +16,26 @@ import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import dev.gould.lingo.api.Destinations; import com.charego.lingo.api.Destinations;
import dev.gould.lingo.common.Game; import com.charego.lingo.common.Game;
import dev.gould.lingo.common.Player; import com.charego.lingo.common.Player;
import dev.gould.lingo.common.Report; import com.charego.lingo.common.Report;
@RestController @RestController
public class PracticeController { public class PracticeController {
private static final Logger log = LoggerFactory.getLogger(PracticeController.class); private static final Logger log = LoggerFactory.getLogger(PracticeController.class);
private final SimpMessagingTemplate messagingTemplate; @Autowired
private final SessionManager sessionManager; private SimpMessagingTemplate messagingTemplate;
private final WordRepository wordRepo;
private final Map<Player, Game> practiceByPlayer = new HashMap<>();
@Autowired @Autowired
public PracticeController(SimpMessagingTemplate messagingTemplate, SessionManager sessionManager, WordRepository wordRepo) { private SessionManager sessionManager;
this.messagingTemplate = messagingTemplate;
this.sessionManager = sessionManager; @Autowired
this.wordRepo = wordRepo; private WordRepository wordRepo;
}
private final Map<Player, Game> practiceByPlayer = new HashMap<>();
@MessageMapping("/practiceGame") @MessageMapping("/practiceGame")
public void practiceGame(@Header(SESSION_ID_HEADER) String sessionId) { public void practiceGame(@Header(SESSION_ID_HEADER) String sessionId) {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.server; package com.charego.lingo.server;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -15,7 +15,7 @@ import org.springframework.web.socket.messaging.AbstractSubProtocolEvent;
import org.springframework.web.socket.messaging.SessionConnectedEvent; import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent; import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import dev.gould.lingo.common.Player; import com.charego.lingo.common.Player;
@Component @Component
public class SessionManager implements ApplicationListener<AbstractSubProtocolEvent> { public class SessionManager implements ApplicationListener<AbstractSubProtocolEvent> {

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.server; package com.charego.lingo.server;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.ChannelRegistration;

View File

@ -1,4 +1,4 @@
package dev.gould.lingo.server; package com.charego.lingo.server;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,9 +10,9 @@ import java.util.Set;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import dev.gould.lingo.common.WordReader; import com.charego.lingo.common.WordReader;
import jakarta.annotation.PostConstruct; import javax.annotation.PostConstruct;
@Component @Component
public class WordRepository { public class WordRepository {

View File

@ -1,7 +1,7 @@
module dev.gould.lingo.server { module com.charego.lingo.server {
requires dev.gould.lingo.common; requires com.charego.lingo.common;
requires dev.gould.lingo.api; requires com.charego.lingo.api;
requires jakarta.annotation; requires java.annotation;
requires org.slf4j; requires org.slf4j;
requires spring.beans; requires spring.beans;
requires spring.boot; requires spring.boot;

View File

@ -1,2 +1,2 @@
# https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html # https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
logging.level.dev.gould.lingo = DEBUG logging.level.com.charego.lingo = DEBUG

View File

@ -7,9 +7,9 @@ const MARGIN_BOTTOM = 75;
let client = null; let client = null;
let sessionId = null; let sessionId = null;
const app = Vue.createApp({ const vm = new Vue({
data() { el: '#vue-app',
return { data: {
players: [], players: [],
games: [], games: [],
messages: [], messages: [],
@ -25,7 +25,6 @@ const app = Vue.createApp({
opponentResults: [], opponentResults: [],
opponentUsername: null, opponentUsername: null,
lastWord: null lastWord: null
}
}, },
computed: { computed: {
inGame: function() { inGame: function() {
@ -276,8 +275,11 @@ const app = Vue.createApp({
this.opponentScore = 0; this.opponentScore = 0;
} }
} }
},
mounted: function() {
document.getElementById('nicknameInput').focus();
} }
}).mount('#app'); });
function afterConnected(stompConnectedFrame) { function afterConnected(stompConnectedFrame) {
console.log('Connected to STOMP endpoint') console.log('Connected to STOMP endpoint')
@ -314,7 +316,7 @@ function main() {
// Will be invoked in case of error encountered at broker. // Will be invoked in case of error encountered at broker.
// Bad login/passcode typically will cause an error. // Bad login/passcode typically will cause an error.
// Compliant brokers will set `message` header with a brief message. Body may contain details. // Complaint brokers will set `message` header with a brief message. Body may contain details.
// Compliant brokers will terminate the connection after any error. // Compliant brokers will terminate the connection after any error.
client.onStompError = function(frame) { client.onStompError = function(frame) {
console.log('Broker reported error: ' + frame.headers['message']); console.log('Broker reported error: ' + frame.headers['message']);
@ -345,10 +347,10 @@ function main() {
const response = JSON.parse(message.body); const response = JSON.parse(message.body);
if (response.success === true) { if (response.success === true) {
console.log('Username: ' + response.username); console.log('Username: ' + response.username);
app.username = response.username; vm.username = response.username;
start(); start();
} else { } else {
app.usernameError = response.errorMessage; vm.usernameError = response.errorMessage;
} }
}; };
@ -356,12 +358,12 @@ function main() {
if (e.key === 'Enter') { if (e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
if (sessionId === null) { if (sessionId === null) {
app.usernameError = 'Not connected to server'; vm.usernameError = 'Not connected to server';
return; return;
} }
const usernameValue = usernameInput.value.trim(); const usernameValue = usernameInput.value.trim();
if (usernameValue.length === 0) { if (usernameValue.length === 0) {
app.usernameError = 'Name cannot be empty'; vm.usernameError = 'Name cannot be empty';
return; return;
} }
if (usernameSubscription === null) { if (usernameSubscription === null) {
@ -377,7 +379,7 @@ function main() {
} }
const usernameValue = usernameInput.value.trim(); const usernameValue = usernameInput.value.trim();
if (usernameValue.length !== 0) { if (usernameValue.length !== 0) {
app.usernameError = ''; vm.usernameError = '';
} }
}); });
} }
@ -407,7 +409,7 @@ function start() {
const games = data.games; const games = data.games;
for (let i = 0; i < games.length; i++) { for (let i = 0; i < games.length; i++) {
const game = games[i]; const game = games[i];
app.games.push({ vm.games.push({
id: game.id, id: game.id,
playerOne: game.playerOne.username, playerOne: game.playerOne.username,
playerTwo: game.playerTwo ? game.playerTwo.username : null, playerTwo: game.playerTwo ? game.playerTwo.username : null,
@ -417,7 +419,7 @@ function start() {
} }
for (let i = 0; i < players.length; i++) { for (let i = 0; i < players.length; i++) {
const player = players[i]; const player = players[i];
app.players.push({ vm.players.push({
username: player.username username: player.username
}); });
} }
@ -436,14 +438,14 @@ function start() {
} }
function addChatAnnouncement(body) { function addChatAnnouncement(body) {
app.messages.push({ vm.messages.push({
body: body body: body
}); });
showNotification('Announcement', body); showNotification('Announcement', body);
} }
function addChatMessage(sender, body) { function addChatMessage(sender, body) {
app.messages.push({ vm.messages.push({
sender: sender, sender: sender,
body: body body: body
}); });
@ -472,7 +474,7 @@ function onChat(message) {
const messageBody = chatMessage.message; const messageBody = chatMessage.message;
if (messageSender === null) { if (messageSender === null) {
addChatAnnouncement(messageBody); addChatAnnouncement(messageBody);
} else if (messageSender === app.username) { } else if (messageSender === vm.username) {
// Ignore messages sent by yourself // Ignore messages sent by yourself
} else { } else {
console.log('Message from ' + messageSender + ': ' + messageBody); console.log('Message from ' + messageSender + ': ' + messageBody);
@ -485,10 +487,10 @@ function onGameClosed(message) {
const gameId = game.id; const gameId = game.id;
const playerOne = game.playerOne.username; const playerOne = game.playerOne.username;
console.log(playerOne + ' closed Game ' + gameId); console.log(playerOne + ' closed Game ' + gameId);
if (playerOne === app.username) { if (playerOne === vm.username) {
app.myGame = null; vm.myGame = null;
} }
app.removeGame(gameId); vm.removeGame(gameId);
} }
function onGameHosted(message) { function onGameHosted(message) {
@ -503,9 +505,9 @@ function onGameHosted(message) {
wordLength: wordLength, wordLength: wordLength,
started: false started: false
}; };
app.games.push(vueGame); vm.games.push(vueGame);
if (playerOne === app.username) { if (playerOne === vm.username) {
app.myGame = vueGame; vm.myGame = vueGame;
} }
} }
@ -519,18 +521,18 @@ function onGameJoined(message) {
console.log(playerTwo + ' joined ' + playerOne + "'s game"); console.log(playerTwo + ' joined ' + playerOne + "'s game");
let vueGame = null; let vueGame = null;
for (let i = 0; i < app.games.length; i++) { for (let i = 0; i < vm.games.length; i++) {
if (app.games[i].id === gameId) { if (vm.games[i].id === gameId) {
app.games[i].playerTwo = playerTwo; vm.games[i].playerTwo = playerTwo;
app.games[i].wordLength = wordLength; vm.games[i].wordLength = wordLength;
app.games[i].started = true; vm.games[i].started = true;
vueGame = app.games[i]; vueGame = vm.games[i];
break; break;
} }
} }
if (playerTwo === app.username) { if (playerTwo === vm.username) {
app.myGame = vueGame; vm.myGame = vueGame;
} }
} }
@ -542,33 +544,33 @@ function onGameLeft(message) {
const gameLeaver = report.gameLeaver.username; const gameLeaver = report.gameLeaver.username;
console.log(gameLeaver + ' left ' + playerOne + "'s game"); console.log(gameLeaver + ' left ' + playerOne + "'s game");
const previousPlayers = []; const previousPlayers = [];
for (let i = 0; i < app.games.length; i++) { for (let i = 0; i < vm.games.length; i++) {
if (app.games[i].id === gameId) { if (vm.games[i].id === gameId) {
previousPlayers.push(app.games[i].playerOne); previousPlayers.push(vm.games[i].playerOne);
previousPlayers.push(app.games[i].playerTwo); previousPlayers.push(vm.games[i].playerTwo);
app.games[i].playerOne = playerOne; vm.games[i].playerOne = playerOne;
app.games[i].playerTwo = game.playerTwo ? game.playerTwo.username : null; vm.games[i].playerTwo = game.playerTwo ? game.playerTwo.username : null;
app.games[i].started = false; vm.games[i].started = false;
break; break;
} }
} }
if (gameLeaver === app.username) { if (gameLeaver === vm.username) {
app.myGame = null; vm.myGame = null;
} }
if (previousPlayers.indexOf(app.username) !== -1) { if (previousPlayers.indexOf(vm.username) !== -1) {
app.opponentUsername = null; vm.opponentUsername = null;
app.lastWord = null; vm.lastWord = null;
app.repaint(); vm.repaint();
} }
} }
function onOpponentJoined(message) { function onOpponentJoined(message) {
const report = JSON.parse(message.body); const report = JSON.parse(message.body);
const firstLetter = report[0]; const firstLetter = report[0];
app.opponentUsername = report[1]; vm.opponentUsername = report[1];
console.log('Opponent username: ' + app.opponentUsername); console.log('Opponent username: ' + vm.opponentUsername);
app.reset(firstLetter, true); vm.reset(firstLetter, true);
app.repaint(); vm.repaint();
} }
function onOpponentReport(message) { function onOpponentReport(message) {
@ -577,14 +579,14 @@ function onOpponentReport(message) {
if (report.correct === true) { if (report.correct === true) {
const guess = report.guess; const guess = report.guess;
const firstLetter = report.firstLetter; const firstLetter = report.firstLetter;
app.opponentScore = app.opponentScore + 100; vm.opponentScore = vm.opponentScore + 100;
app.lastWord = guess; vm.lastWord = guess;
app.reset(firstLetter, false); vm.reset(firstLetter, false);
app.repaint(); vm.repaint();
} else { } else {
const result = report.result; const result = report.result;
app.opponentResults.push(result); vm.opponentResults.push(result);
app.repaint(); vm.repaint();
} }
} }
@ -594,26 +596,26 @@ function onPlayerReport(message) {
if (report.correct === true) { if (report.correct === true) {
const guess = report.guess; const guess = report.guess;
const firstLetter = report.firstLetter; const firstLetter = report.firstLetter;
app.myScore = app.myScore + 100; vm.myScore = vm.myScore + 100;
app.lastWord = guess; vm.lastWord = guess;
app.reset(firstLetter, false); vm.reset(firstLetter, false);
app.repaint(); vm.repaint();
} else { } else {
const guess = report.guess; const guess = report.guess;
const result = report.result; const result = report.result;
if (result[0] === 9) { if (result[0] === 9) {
const invalidGuess = '-'.repeat(app.wordLength); const invalidGuess = '-'.repeat(vm.wordLength);
app.myGuesses.push(invalidGuess); vm.myGuesses.push(invalidGuess);
} else { } else {
for (let i = 0; i < app.wordLength; i++) { for (let i = 0; i < vm.wordLength; i++) {
if (result[i] === 2) { if (result[i] === 2) {
app.myProgress[i] = guess[i]; vm.myProgress[i] = guess[i];
} }
} }
app.myGuesses.push(guess); vm.myGuesses.push(guess);
} }
app.myResults.push(result); vm.myResults.push(result);
app.repaint(); vm.repaint();
} }
} }
@ -621,7 +623,7 @@ function onPlayerJoined(message) {
const report = JSON.parse(message.body); const report = JSON.parse(message.body);
const username = report[0]; const username = report[0];
const numUsers = report[1]; const numUsers = report[1];
if (username === app.username) { if (username === vm.username) {
addChatAnnouncement('Welcome to Lingo!'); addChatAnnouncement('Welcome to Lingo!');
if (numUsers === 1) { if (numUsers === 1) {
addChatAnnouncement('You are the only player online'); addChatAnnouncement('You are the only player online');
@ -630,7 +632,7 @@ function onPlayerJoined(message) {
} }
} else { } else {
addChatAnnouncement(username + ' joined'); addChatAnnouncement(username + ' joined');
app.players.push({ vm.players.push({
username: username username: username
}); });
} }
@ -639,7 +641,7 @@ function onPlayerJoined(message) {
function onPlayerLeft(message) { function onPlayerLeft(message) {
const username = message.body; const username = message.body;
addChatAnnouncement(username + ' left'); addChatAnnouncement(username + ' left');
app.removePlayer(username); vm.removePlayer(username);
} }
function canShowNotification() { function canShowNotification() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View File

@ -1,76 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4f, 2020-05-01)"
sodipodi:docname="logo.svg"
id="svg8"
version="1.1"
viewBox="0 0 64 64"
height="64mm"
width="64mm">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="0"
inkscape:window-y="23"
inkscape:window-x="0"
inkscape:window-height="791"
inkscape:window-width="1252"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="291.42857"
inkscape:cx="297.14282"
inkscape:zoom="0.35"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(-13.607147,-35.529762)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<circle
r="32"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
cy="67.529762"
cx="45.607147"
id="path16"
style="fill:#000000;stroke-width:0.260465" />
<text
transform="scale(1.0355499,0.96567047)"
id="text30"
y="87.83168"
x="22.764704"
style="font-style:normal;font-weight:normal;font-size:52.1622px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.271678"
xml:space="preserve"><tspan
style="font-size:52.1622px;fill:#ffffff;stroke-width:0.271678"
y="87.83168"
x="22.764704"
id="tspan28"
sodipodi:role="line">G</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -3,20 +3,19 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Lingo</title> <title>Lingo</title>
<link rel="icon" href="favicon.svg" sizes="any" type="image/svg+xml">
<link rel="stylesheet" href="layout.css"> <link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="//cdn.jsdelivr.net/npm/vue@3.0.7/dist/vue.global.min.js"></script> <script src="//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/@stomp/stompjs@6.1.0/bundles/stomp.umd.min.js"></script> <script src="//cdn.jsdelivr.net/npm/@stomp/stompjs@5.4.4/bundles/stomp.umd.min.js"></script>
</head> </head>
<body> <body>
<div id="app" v-cloak> <div id="vue-app" v-cloak>
<div class="main column"> <div class="main column">
<div class="header row">Lingo</div> <div class="header row">Lingo</div>
<div class="body row nofooter"> <div class="body row nofooter">
<div v-show="!username" class="form"> <div v-show="!username" class="form">
<h2>What is your name?</h2> <h2>What is your name?</h2>
<input id="nicknameInput" type="text" class="form-control" maxlength="16" v-bind:autofocus="'autofocus'"> <input id="nicknameInput" type="text" class="form-control" maxlength="16">
<p class="error-message">{{ usernameError }}</p> <p class="error-message">{{ usernameError }}</p>
</div> </div>
<div v-show="username"> <div v-show="username">

View File

@ -11,7 +11,7 @@
<button id="skipButton" type="button">Skip Word</button> <button id="skipButton" type="button">Skip Word</button>
</div> </div>
<script src="//cdn.jsdelivr.net/npm/@stomp/stompjs@6.0.0/bundles/stomp.umd.min.js"></script> <script src="//cdn.jsdelivr.net/npm/@stomp/stompjs@5.2.0/bundles/stomp.umd.min.js"></script>
<script src="practice.js"></script> <script src="practice.js"></script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,6 @@
const HEIGHT = 300; const HEIGHT = 300;
const WIDTH = 250; const WIDTH = 250;
const CHARACTER_HEIGHT = 50; const SIDE = 50;
const MARGIN_TOP = 50; const MARGIN_TOP = 50;
const MARGIN_BOTTOM = 25; const MARGIN_BOTTOM = 25;
@ -122,11 +122,11 @@ function drawScore(x, y, score) {
function drawGrid(xOrigin, yOrigin) { function drawGrid(xOrigin, yOrigin) {
ctx.beginPath(); ctx.beginPath();
for (let x = 0; x <= WIDTH; x += CHARACTER_HEIGHT) { for (let x = 0; x <= WIDTH; x += SIDE) {
ctx.moveTo(xOrigin + x, yOrigin); ctx.moveTo(xOrigin + x, yOrigin);
ctx.lineTo(xOrigin + x, yOrigin + HEIGHT); ctx.lineTo(xOrigin + x, yOrigin + HEIGHT);
} }
for (let y = 0; y <= HEIGHT; y += CHARACTER_HEIGHT) { for (let y = 0; y <= HEIGHT; y += SIDE) {
ctx.moveTo(xOrigin, yOrigin + y); ctx.moveTo(xOrigin, yOrigin + y);
ctx.lineTo(xOrigin + WIDTH, yOrigin + y); ctx.lineTo(xOrigin + WIDTH, yOrigin + y);
} }
@ -136,43 +136,43 @@ function drawGrid(xOrigin, yOrigin) {
function drawInput(xOrigin, yOrigin, input) { function drawInput(xOrigin, yOrigin, input) {
ctx.fillStyle = 'green'; ctx.fillStyle = 'green';
let x = xOrigin + CHARACTER_HEIGHT * 0.5; let x = xOrigin + SIDE * 0.5;
const y = yOrigin + CHARACTER_HEIGHT * 0.5; const y = yOrigin + SIDE * 0.5;
for (let i = 0; i < myGuess.length; i++) { for (let i = 0; i < myGuess.length; i++) {
ctx.fillText(myGuess[i], x, y); ctx.fillText(myGuess[i], x, y);
x += CHARACTER_HEIGHT; x += SIDE;
} }
} }
function drawGuesses(xOrigin, yOrigin, guesses, results) { function drawGuesses(xOrigin, yOrigin, guesses, results) {
let y = yOrigin + CHARACTER_HEIGHT * 1.5; let y = yOrigin + SIDE * 1.5;
const numGuesses = Math.min(4, guesses.length); const numGuesses = Math.min(4, guesses.length);
for (let i = 0; i < numGuesses; i++) { for (let i = 0; i < numGuesses; i++) {
let x = xOrigin + CHARACTER_HEIGHT * 0.5; let x = xOrigin + SIDE * 0.5;
const guess = guesses[guesses.length - numGuesses + i]; const guess = guesses[guesses.length - numGuesses + i];
const result = results[results.length - numGuesses + i]; const result = results[results.length - numGuesses + i];
for (let j = 0; j < 5; j++) { for (let j = 0; j < 5; j++) {
if (result[j] === 1) { if (result[j] === 1) {
ctx.fillStyle = 'yellow'; ctx.fillStyle = 'yellow';
ctx.fillRect(x - CHARACTER_HEIGHT * 0.5, y - CHARACTER_HEIGHT * 0.5, CHARACTER_HEIGHT, CHARACTER_HEIGHT); ctx.fillRect(x - SIDE * 0.5, y - SIDE * 0.5, SIDE, SIDE);
} else if (result[j] === 2) { } else if (result[j] === 2) {
ctx.fillStyle = 'orange'; ctx.fillStyle = 'orange';
ctx.fillRect(x - CHARACTER_HEIGHT * 0.5, y - CHARACTER_HEIGHT * 0.5, CHARACTER_HEIGHT, CHARACTER_HEIGHT); ctx.fillRect(x - SIDE * 0.5, y - SIDE * 0.5, SIDE, SIDE);
} }
ctx.fillStyle = 'green'; ctx.fillStyle = 'green';
ctx.fillText(guess[j], x, y); ctx.fillText(guess[j], x, y);
x += CHARACTER_HEIGHT; x += SIDE;
} }
y += CHARACTER_HEIGHT; y += SIDE;
} }
return y; return y;
} }
function drawHint(xOrigin, yOrigin, progress) { function drawHint(xOrigin, yOrigin, progress) {
let x = xOrigin + CHARACTER_HEIGHT * 0.5; let x = xOrigin + SIDE * 0.5;
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
ctx.fillText(progress[i], x, yOrigin); ctx.fillText(progress[i], x, yOrigin);
x += CHARACTER_HEIGHT; x += SIDE;
} }
} }

View File

@ -1 +1 @@
java.runtime.version=17 java.runtime.version=11