Begin implementing Rainbow Shootout and refactor other classes

This commit is contained in:
Julia Butenhoff 2022-08-24 14:15:36 -05:00 committed by Julia Butenhoff
parent 42a34230ec
commit c430517f7b
20 changed files with 429 additions and 69 deletions

View File

@ -2,6 +2,8 @@ package com.icedberries.UBFunkeysServer.ArkOne;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.BasePlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.GalaxyPlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.Multiplayer.MultiplayerPlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.Multiplayer.RainbowShootoutPlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.TrunkPlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.UserPlugin;
import com.icedberries.UBFunkeysServer.domain.User;
@ -11,26 +13,19 @@ import javagrinko.spring.tcp.Server;
import javagrinko.spring.tcp.TcpController;
import javagrinko.spring.tcp.TcpHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.w3c.dom.Element;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@TcpController
@EnableScheduling
public class ArkOneController implements TcpHandler {
public static final String IP_ADDRESS = "127.0.0.1";
private boolean firstOfflineScheduledRun = true;
@Autowired
Server server;
@ -54,6 +49,12 @@ public class ArkOneController implements TcpHandler {
@Autowired
TrunkPlugin trunkPlugin;
@Autowired
MultiplayerPlugin multiplayerPlugin;
@Autowired
RainbowShootoutPlugin rainbowShootoutPlugin;
@Override
public void receiveData(Connection connection, byte[] data) {
// Log the received request
@ -66,12 +67,15 @@ public class ArkOneController implements TcpHandler {
// Parse the incoming data into individual commands
List<String> commands = ArkOneParser.ParseReceivedMessage(xmlData);
// Parse out the plugin from the routing string (if it exists)
String[] routingString = ArkOneParser.ParseRoutingStrings(xmlData);
// Handle each command
for (String command : commands) {
try {
Element commandInfo = (Element)ArkOneParser.ParseCommand(command);
switch(commandInfo.getNodeName()) {
// Plugin 0 - Core
// ----------------------------- Plugin 0 (Core) ---------------------------- \\
case "a_lgu":
responses.add(basePlugin.LoginGuestUser());
break;
@ -85,7 +89,7 @@ public class ArkOneController implements TcpHandler {
responses.add(basePlugin.LoginRegisteredUser(commandInfo, connection.getClientIdentifier()));
break;
// Plugin 1 (User)
// ----------------------------- Plugin 1 (User) ---------------------------- \\
case "u_reg":
responses.add(userPlugin.RegisterUser(commandInfo));
break;
@ -113,20 +117,31 @@ public class ArkOneController implements TcpHandler {
case "u_dbr":
responses.add(userPlugin.DeleteBuddyResponse(commandInfo, connection));
break;
case "u_inv":
responses.add(userPlugin.InvitePlayer());
break;
case "u_inr":
responses.add(userPlugin.InviteBuddyResponse());
break;
case "p":
responses.add(userPlugin.Ping(connection));
break;
// Plugin 7 (Galaxy)
// ----------------------- Plugin 5 (Rainbow Shootout) ---------------------- \\
case "cm":
responses.add(rainbowShootoutPlugin.CharacterMove(commandInfo));
break;
case "bs":
responses.add(rainbowShootoutPlugin.BlockShot());
break;
// ---------------------------- Plugin 7 (Galaxy) --------------------------- \\
case "lpv":
responses.add(galaxyPlugin.LoadProfileVersion(connection));
break;
case "vsu":
responses.add(galaxyPlugin.VersionStatisticsRequest());
break;
case "sp":
responses.add(galaxyPlugin.SaveProfile(commandInfo, connection));
break;
case "spp":
responses.add(galaxyPlugin.SaveProfilePart(commandInfo, connection));
break;
@ -137,7 +152,7 @@ public class ArkOneController implements TcpHandler {
responses.add(galaxyPlugin.GetLeaderboardStats(commandInfo, connection));
break;
// Plugin 10 (Trunk)
// ---------------------------- Plugin 10 (Trunk) --------------------------- \\
case "gua":
responses.add(trunkPlugin.GetUserAssets(connection));
break;
@ -187,7 +202,42 @@ public class ArkOneController implements TcpHandler {
responses.add(trunkPlugin.BuyItem(commandInfo, connection));
break;
// Catch Unhandled Commands
// ----------------------- Multiplayer (Shared by all) ---------------------- \\
case "lv":
responses.add(multiplayerPlugin.LeaveGame());
break;
case "rp":
responses.add(multiplayerPlugin.ReadyPlay());
break;
case "ms":
responses.add(multiplayerPlugin.MessageOpponent(commandInfo, connection, routingString[1]));
break;
case "pa":
responses.add(multiplayerPlugin.PlayAgain());
// ---------------------------- Conflict Commands --------------------------- \\
case "jn":
if (routingString[1].equals("2")) {
//TODO: IMPLEMENT CHAT - For now throw unhandled
responses.add("<unknown />");
System.out.println("[ArkOne][ERROR] Unhandled command: " + commandInfo.getNodeName());
//responses.add(chatPlugin.JoinChat());
} else {
responses.add(multiplayerPlugin.JoinGame());
}
break;
case "sp":
switch(routingString[1]) {
case "5":
responses.add(rainbowShootoutPlugin.ShotParameters(commandInfo));
break;
case "7":
responses.add(galaxyPlugin.SaveProfile(commandInfo, connection));
break;
}
break;
// -------------------------------------------------------------------------- \\
default:
responses.add("<unknown />");
System.out.println("[ArkOne][ERROR] Unhandled command: " + commandInfo.getNodeName());
@ -247,40 +297,4 @@ public class ArkOneController implements TcpHandler {
}
}
}
// Run this every 60 seconds to check for inactive online users
@Scheduled(fixedRate = 60000)
public void setOfflineInactiveUsers() {
List<User> onlineUsers = userService.getOnlineUsers();
// If there is at least one user in the list
if (onlineUsers.size() > 0) {
for (User user : onlineUsers) {
// Make sure the user has a last ping else turn them offline
// A ping should be set on login
if (firstOfflineScheduledRun) {
user.setIsOnline(0);
userService.save(user);
continue;
}
if (user.getLastPing() == null) {
user.setIsOnline(0);
userService.save(user);
continue;
}
// Calculate how many milliseconds since last ping/login
long difference = Math.abs(Duration.between(user.getLastPing(), LocalDateTime.now()).toMillis());
if (difference > 60000) {
// USer has been online for more than 60 seconds without a new ping - set them offline
user.setIsOnline(0);
userService.save(user);
}
}
}
if (firstOfflineScheduledRun) {
firstOfflineScheduledRun = false;
}
}
}

View File

@ -96,4 +96,8 @@ public class ArkOneParser {
// Nothing found
return null;
}
public static String[] ParseRoutingStrings(String command) {
}
}

View File

@ -0,0 +1,58 @@
package com.icedberries.UBFunkeysServer.ArkOne.Plugins.Multiplayer;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneParser;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneSender;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import javagrinko.spring.tcp.Connection;
import javagrinko.spring.tcp.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
@Service
public class MultiplayerPlugin {
@Autowired
Server server;
@Autowired
UserService userService;
@Autowired
private ArkOneSender arkOneSender;
public String MessageOpponent(Element element, Connection connection, String plugin)
throws ParserConfigurationException, TransformerException {
// Start building the response with the plugin tag
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document resp = dBuilder.newDocument();
Element rootElement = resp.createElement("h" + plugin + "_0");
resp.appendChild(rootElement);
// Get a reference to the calling user
User thisUser = server.getConnectedUsers().get(connection.getClientIdentifier());
// Create the message element
Element msElement = resp.createElement("ms");
msElement.setAttribute("n", thisUser.getUsername());
msElement.setAttribute("m", element.getAttribute("m"));
msElement.setAttribute("bid", element.getAttribute("bid"));
rootElement.appendChild(msElement);
// Send the message to the other user
User buddy = userService.findByUUID(Integer.valueOf(element.getAttribute("bid"))).orElse(null);
if (buddy != null) {
arkOneSender.SendToUser(buddy.getConnectionId(), ArkOneParser.RemoveXMLTag(resp));
}
return ArkOneParser.RemoveXMLTag(resp);
}
}

View File

@ -0,0 +1,81 @@
package com.icedberries.UBFunkeysServer.ArkOne.Plugins.Multiplayer;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneParser;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneSender;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import javagrinko.spring.tcp.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
@Service
public class RainbowShootoutPlugin {
@Autowired
Server server;
@Autowired
UserService userService;
@Autowired
private ArkOneSender arkOneSender;
public String ShotParameters(Element element) throws ParserConfigurationException,
TransformerException {
// Start building the response with the plugin tag
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document resp = dBuilder.newDocument();
Element rootElement = resp.createElement("h5_0");
resp.appendChild(rootElement);
// Create the message element
Element spElement = resp.createElement("sp");
spElement.setAttribute("p", element.getAttribute("p"));
spElement.setAttribute("z", element.getAttribute("z"));
spElement.setAttribute("y", element.getAttribute("y"));
spElement.setAttribute("x", element.getAttribute("x"));
spElement.setAttribute("bid", element.getAttribute("bid"));
rootElement.appendChild(spElement);
// Send the message to the other user
User buddy = userService.findByUUID(Integer.valueOf(element.getAttribute("bid"))).orElse(null);
if (buddy != null) {
arkOneSender.SendToUser(buddy.getConnectionId(), ArkOneParser.RemoveXMLTag(resp));
}
return "<notneeded/>";
}
public String CharacterMove(Element element) throws ParserConfigurationException,
TransformerException {
// Start building the response with the plugin tag
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document resp = dBuilder.newDocument();
Element rootElement = resp.createElement("h5_0");
resp.appendChild(rootElement);
// Create the message element
Element cmElement = resp.createElement("cm");
cmElement.setAttribute("x", element.getAttribute("x"));
cmElement.setAttribute("d", element.getAttribute("d"));
cmElement.setAttribute("bid", element.getAttribute("bid"));
rootElement.appendChild(cmElement);
// Send the message to the other user
User buddy = userService.findByUUID(Integer.valueOf(element.getAttribute("bid"))).orElse(null);
if (buddy != null) {
arkOneSender.SendToUser(buddy.getConnectionId(), ArkOneParser.RemoveXMLTag(resp));
}
return "<notneeded/>";
}
}

View File

@ -201,7 +201,7 @@ public class UserPlugin {
User buddy = userService.findByUsername(element.getAttribute("n")).orElse(null);
User thisUser = server.getConnectedUsers().get(connection.getClientIdentifier());
boolean fail = false;
boolean failed = false;
boolean isAlreadyBuddy = false;
if (buddy != null && buddy.getRawBuddyList() != null) {
@ -223,23 +223,23 @@ public class UserPlugin {
if (buddy == null) {
rootElement.setAttribute("r", "2");
fail = true;
failed = true;
} else if (buddy.getIsOnline() == 0) {
rootElement.setAttribute("r", "5");
fail = true;
failed = true;
} else if (isAlreadyBuddy) {
rootElement.setAttribute("r", "3");
fail = true;
failed = true;
} else if (buddy.getUsername().equals("") || buddy.getUsername().equals("GUESTUSER")) {
rootElement.setAttribute("r", "2");
fail = true;
failed = true;
}
rootElement.setAttribute("n", element.getAttribute("n"));
resp.appendChild(rootElement);
// Check if failed
if (fail) {
if (failed) {
return ArkOneParser.RemoveXMLTag(resp);
} else {
Document send = dBuilder.newDocument();

View File

@ -0,0 +1,76 @@
package com.icedberries.UBFunkeysServer.config;
import com.icedberries.UBFunkeysServer.domain.Multiplayer.RainbowShootout;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.RainbowShootoutService;
import com.icedberries.UBFunkeysServer.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
@EnableScheduling
@Component
public class DatabaseCleanup {
@Autowired
UserService userService;
@Autowired
RainbowShootoutService rainbowShootoutService;
private boolean firstOfflineScheduledRun = true;
// Run this every 60 seconds to check for inactive online users
@Scheduled(fixedRate = 60000)
public void setOfflineInactiveUsers() {
List<User> onlineUsers = userService.getOnlineUsers();
// If there is at least one user in the list
if (onlineUsers.size() > 0) {
for (User user : onlineUsers) {
// Make sure the user has a last ping else turn them offline
// A ping should be set on login
if (firstOfflineScheduledRun) {
user.setIsOnline(0);
userService.save(user);
continue;
}
if (user.getLastPing() == null && user.getIsOnline() != 0) {
user.setIsOnline(0);
userService.save(user);
continue;
}
// Calculate how many milliseconds since last ping/login
long difference = Math.abs(Duration.between(user.getLastPing(), LocalDateTime.now()).toMillis());
if (difference > 60000) {
// USer has been online for more than 60 seconds without a new ping - set them offline
user.setIsOnline(0);
userService.save(user);
}
}
}
if (firstOfflineScheduledRun) {
firstOfflineScheduledRun = false;
}
}
// Cleanup open Multiplayer games that could be sitting open every 15 minutes and on startup
// NOTE: This could create issues where a user is no longer matchmaking and will have to restart looking
@Scheduled(fixedRate = 900000)
public void clearOpenMultiplayerMatchmaking() {
// Get all open multiplayer matchmaking entries in all tables
List<RainbowShootout> openRainbowShootout = rainbowShootoutService.findAll();
// Iterate over each game's entries
for (RainbowShootout rainbowShootout : openRainbowShootout) {
rainbowShootoutService.delete(rainbowShootout);
}
}
}

View File

@ -0,0 +1,14 @@
package com.icedberries.UBFunkeysServer.config;
public class TableNames {
public static final String CLEANING = "Cleanings";
public static final String CRIB = "Cribs";
public static final String FAMILIAR = "Familiars";
public static final String ITEM = "Items";
public static final String JAMMER = "Jammers";
public static final String LEVEL = "GameMakerLevels";
public static final String MOOD = "Moods";
public static final String RAINBOW_SHOOTOUT = "RainbowShootout";
public static final String USER = "Users";
}

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.*;
import javax.persistence.Entity;
@ -12,7 +13,7 @@ import javax.persistence.Table;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Cleanings")
@Table(name = TableNames.CLEANING)
public class Cleaning {
// DB ID

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
@ -15,7 +16,7 @@ import javax.persistence.*;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Cribs")
@Table(name = TableNames.CRIB)
public class Crib {
@Id

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.*;
import javax.persistence.Entity;
@ -12,7 +13,7 @@ import javax.persistence.Table;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Familiars")
@Table(name = TableNames.FAMILIAR)
public class Familiar {
// DB ID

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.*;
import javax.persistence.Entity;
@ -12,7 +13,7 @@ import javax.persistence.Table;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Items")
@Table(name = TableNames.ITEM)
public class Item {
// DB ID

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.*;
import javax.persistence.Entity;
@ -12,7 +13,7 @@ import javax.persistence.Table;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Jammers")
@Table(name = TableNames.JAMMER)
public class Jammer {
// DB ID

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.*;
import org.hibernate.annotations.Type;
@ -12,7 +13,7 @@ import java.time.LocalDateTime;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "GameMakerLevels")
@Table(name = TableNames.LEVEL)
public class Level {
@Id

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.*;
import javax.persistence.Entity;
@ -12,7 +13,7 @@ import javax.persistence.Table;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Moods")
@Table(name = TableNames.MOOD)
public class Mood {
// DB ID

View File

@ -0,0 +1,55 @@
package com.icedberries.UBFunkeysServer.domain.Multiplayer;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = TableNames.RAINBOW_SHOOTOUT)
public class RainbowShootout {
@Id
private Integer userId;
private String username;
private String connectionId;
// Determines whether the user is matchmaking or not
private Integer challenge;
// UserId of the opponent
private Integer challenger;
// A string that contains the opponent's bittyID/Funkey and a number at the end that doesn't seem to have a use
private String challengerInfo;
// Same as challengerInfo, but for the player. Used for random matchmaking
private String playerInfo;
// The player's score
private Integer score;
// If the user is ready to start the round
private String ready;
public java.util.UUID getConnectionId() {
return java.util.UUID.fromString(connectionId);
}
public void setConnectionId(java.util.UUID newUUID) {
connectionId = newUUID.toString();
}
}

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.domain;
import com.icedberries.UBFunkeysServer.config.TableNames;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
@ -13,10 +14,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.UUID;
@Getter
@Setter
@ -24,7 +22,7 @@ import java.util.UUID;
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "Users")
@Table(name = TableNames.USER)
public class User {
// User ID

View File

@ -0,0 +1,15 @@
package com.icedberries.UBFunkeysServer.repository;
import com.icedberries.UBFunkeysServer.domain.Multiplayer.RainbowShootout;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RainbowShootoutRepository extends CrudRepository<RainbowShootout, Integer> {
List<RainbowShootout> findAll();
}

View File

@ -0,0 +1,12 @@
package com.icedberries.UBFunkeysServer.service;
import com.icedberries.UBFunkeysServer.domain.Multiplayer.RainbowShootout;
import java.util.List;
public interface RainbowShootoutService {
List<RainbowShootout> findAll();
void delete(RainbowShootout rainbowShootout);
}

View File

@ -0,0 +1,26 @@
package com.icedberries.UBFunkeysServer.service.impl;
import com.icedberries.UBFunkeysServer.domain.Multiplayer.RainbowShootout;
import com.icedberries.UBFunkeysServer.repository.RainbowShootoutRepository;
import com.icedberries.UBFunkeysServer.service.RainbowShootoutService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class RainbowShootoutServiceImpl implements RainbowShootoutService {
private final RainbowShootoutRepository rainbowShootoutRepository;
@Override
public List<RainbowShootout> findAll() {
return rainbowShootoutRepository.findAll();
}
@Override
public void delete(RainbowShootout rainbowShootout) {
rainbowShootoutRepository.delete(rainbowShootout);
}
}

View File

@ -17,7 +17,7 @@ import java.util.UUID;
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
public final UserRepository userRepository;
private final UserRepository userRepository;
@Autowired
Server server;