mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-22 07:37:43 +00:00
Implement basic testing (login & HTTP status)
This commit is contained in:
parent
5b5ec9b6b4
commit
2efa022d83
@ -1,7 +1,5 @@
|
||||
package emu.grasscutter.database;
|
||||
|
||||
import static com.mongodb.client.model.Filters.eq;
|
||||
|
||||
import dev.morphia.query.*;
|
||||
import dev.morphia.query.experimental.filters.Filters;
|
||||
import emu.grasscutter.*;
|
||||
@ -20,24 +18,19 @@ import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.quest.GameMainQuest;
|
||||
import emu.grasscutter.game.world.SceneGroupInstance;
|
||||
import emu.grasscutter.utils.objects.Returnable;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
|
||||
import static com.mongodb.client.model.Filters.eq;
|
||||
|
||||
public final class DatabaseHelper {
|
||||
@Getter
|
||||
private static final ExecutorService eventExecutor =
|
||||
new ThreadPoolExecutor(
|
||||
6,
|
||||
6,
|
||||
60,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingDeque<>(),
|
||||
FastThreadLocalThread::new,
|
||||
new ThreadPoolExecutor.AbortPolicy());
|
||||
Executors.newFixedThreadPool(4);
|
||||
|
||||
/**
|
||||
* Saves an object on the account datastore.
|
||||
|
@ -12,12 +12,13 @@ import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.net.proto.AchievementOuterClass.Achievement.Status;
|
||||
import emu.grasscutter.server.event.player.PlayerCompleteAchievementEvent;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import lombok.*;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntSupplier;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.*;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
@Entity("achievements")
|
||||
@Data
|
||||
@ -44,15 +45,30 @@ public class Achievements {
|
||||
return achievements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blank achievements object.
|
||||
*
|
||||
* @return The achievements object.
|
||||
*/
|
||||
public static Achievements blank() {
|
||||
return Achievements.of()
|
||||
.achievementList(init())
|
||||
.finishedAchievementNum(0)
|
||||
.takenGoalRewardIdList(Lists.newArrayList())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and saves a blank achievements object.
|
||||
*
|
||||
* @param uid The UID of the player.
|
||||
* @return The achievements object.
|
||||
*/
|
||||
public static Achievements create(int uid) {
|
||||
var newAchievement =
|
||||
Achievements.of()
|
||||
.uid(uid)
|
||||
.achievementList(init())
|
||||
.finishedAchievementNum(0)
|
||||
.takenGoalRewardIdList(Lists.newArrayList())
|
||||
.build();
|
||||
var newAchievement = blank();
|
||||
newAchievement.setUid(uid);
|
||||
newAchievement.save();
|
||||
|
||||
return newAchievement;
|
||||
}
|
||||
|
||||
|
@ -14,11 +14,12 @@ import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePass
|
||||
import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule;
|
||||
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import lombok.Getter;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.time.*;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.*;
|
||||
import lombok.Getter;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
@Entity(value = "battlepass", useDiscriminator = false)
|
||||
public class BattlePassManager extends BasePlayerDataManager {
|
||||
@ -40,7 +41,10 @@ public class BattlePassManager extends BasePlayerDataManager {
|
||||
|
||||
public BattlePassManager(Player player) {
|
||||
super(player);
|
||||
|
||||
this.ownerUid = player.getUid();
|
||||
this.missions = new HashMap<>();
|
||||
this.takenRewards = new HashMap<>();
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
|
@ -8,7 +8,7 @@ import emu.grasscutter.data.excels.world.WeatherData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.*;
|
||||
import emu.grasscutter.game.ability.AbilityManager;
|
||||
import emu.grasscutter.game.achievement.Achievements;
|
||||
import emu.grasscutter.game.achievement.*;
|
||||
import emu.grasscutter.game.activity.ActivityManager;
|
||||
import emu.grasscutter.game.avatar.*;
|
||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||
@ -259,6 +259,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
|
||||
|
||||
this.birthday = new PlayerBirthday();
|
||||
this.achievements = Achievements.blank();
|
||||
this.rewardedLevels = new HashSet<>();
|
||||
this.homeRewardedLevels = new HashSet<>();
|
||||
this.seenRealmList = new HashSet<>();
|
||||
@ -273,8 +274,10 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.energyManager = new EnergyManager(this);
|
||||
this.resinManager = new ResinManager(this);
|
||||
this.forgingManager = new ForgingManager(this);
|
||||
this.deforestationManager = new DeforestationManager(this);
|
||||
this.progressManager = new PlayerProgressManager(this);
|
||||
this.furnitureManager = new FurnitureManager(this);
|
||||
this.battlePassManager = new BattlePassManager(this);
|
||||
this.cookingManager = new CookingManager(this);
|
||||
this.cookingCompoundManager = new CookingCompoundManager(this);
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
@ -297,19 +300,6 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.applyProperties();
|
||||
this.getFlyCloakList().add(140001);
|
||||
this.getNameCardList().add(210001);
|
||||
|
||||
this.mapMarksManager = new MapMarksManager(this);
|
||||
this.staminaManager = new StaminaManager(this);
|
||||
this.sotsManager = new SotSManager(this);
|
||||
this.energyManager = new EnergyManager(this);
|
||||
this.resinManager = new ResinManager(this);
|
||||
this.deforestationManager = new DeforestationManager(this);
|
||||
this.forgingManager = new ForgingManager(this);
|
||||
this.progressManager = new PlayerProgressManager(this);
|
||||
this.furnitureManager = new FurnitureManager(this);
|
||||
this.cookingManager = new CookingManager(this);
|
||||
this.cookingCompoundManager = new CookingCompoundManager(this);
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,8 +1,5 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import static emu.grasscutter.GameConstants.DEBUG;
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.*;
|
||||
@ -15,12 +12,16 @@ import emu.grasscutter.net.proto.GivingRecordOuterClass.GivingRecord;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import lombok.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
import lombok.*;
|
||||
|
||||
import static emu.grasscutter.GameConstants.DEBUG;
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
public final class QuestManager extends BasePlayerManager {
|
||||
@Getter private final Player player;
|
||||
@ -571,7 +572,7 @@ public final class QuestManager extends BasePlayerManager {
|
||||
* @param quest The ID of the quest.
|
||||
*/
|
||||
public void checkQuestAlreadyFulfilled(GameQuest quest) {
|
||||
Grasscutter.getThreadPool()
|
||||
eventExecutor
|
||||
.submit(
|
||||
() -> {
|
||||
for (var condition : quest.getQuestData().getFinishCond()) {
|
||||
|
@ -1,60 +1,18 @@
|
||||
package io.grasscutter;
|
||||
|
||||
import com.mchange.util.AssertException;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.config.Configuration;
|
||||
import java.io.IOException;
|
||||
import lombok.Getter;
|
||||
import io.grasscutter.virtual.*;
|
||||
import lombok.*;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Testing entrypoint for {@link Grasscutter}. */
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public final class GrasscutterTest {
|
||||
@Getter private static final OkHttpClient httpClient = new OkHttpClient();
|
||||
@Getter
|
||||
public static final OkHttpClient httpClient = new OkHttpClient();
|
||||
@Getter public static final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Getter private static int httpPort = -1;
|
||||
@Getter private static int gamePort = -1;
|
||||
|
||||
/**
|
||||
* Creates an HTTP URL.
|
||||
*
|
||||
* @param route The route to use.
|
||||
* @return The URL.
|
||||
*/
|
||||
public static String http(String route) {
|
||||
return "http://127.0.0.1:" + GrasscutterTest.httpPort + "/" + route;
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void entry() {
|
||||
try {
|
||||
// Start Grasscutter.
|
||||
Grasscutter.main(new String[] {"-test"});
|
||||
} catch (Exception ignored) {
|
||||
throw new AssertException("Grasscutter failed to start.");
|
||||
}
|
||||
|
||||
// Set the ports.
|
||||
GrasscutterTest.httpPort = Configuration.SERVER.http.bindPort;
|
||||
GrasscutterTest.gamePort = Configuration.SERVER.game.bindPort;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("HTTP server check")
|
||||
public void checkHttpServer() {
|
||||
// Create a request.
|
||||
var request = new Request.Builder().url(GrasscutterTest.http("")).build();
|
||||
|
||||
// Perform the request.
|
||||
try (var response = GrasscutterTest.httpClient.newCall(request).execute()) {
|
||||
// Check the response.
|
||||
Assertions.assertTrue(response.isSuccessful());
|
||||
} catch (IOException exception) {
|
||||
throw new AssertionError(exception);
|
||||
}
|
||||
}
|
||||
@Getter public static VirtualAccount account;
|
||||
@Setter
|
||||
@Getter public static VirtualPlayer player;
|
||||
@Getter public static VirtualGameSession gameSession;
|
||||
}
|
||||
|
17
src/test/java/io/grasscutter/TestUtils.java
Normal file
17
src/test/java/io/grasscutter/TestUtils.java
Normal file
@ -0,0 +1,17 @@
|
||||
package io.grasscutter;
|
||||
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import emu.grasscutter.utils.objects.Returnable;
|
||||
|
||||
public interface TestUtils {
|
||||
/**
|
||||
* Waits for a condition to be met.
|
||||
*
|
||||
* @param condition The condition.
|
||||
*/
|
||||
static void waitFor(Returnable<Boolean> condition) {
|
||||
while (!condition.invoke()) {
|
||||
Utils.sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
61
src/test/java/io/grasscutter/tests/BaseServerTest.java
Normal file
61
src/test/java/io/grasscutter/tests/BaseServerTest.java
Normal file
@ -0,0 +1,61 @@
|
||||
package io.grasscutter.tests;
|
||||
|
||||
import com.mchange.util.AssertException;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.config.Configuration;
|
||||
import io.grasscutter.GrasscutterTest;
|
||||
import io.grasscutter.virtual.*;
|
||||
import lombok.*;
|
||||
import okhttp3.*;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Testing entrypoint for {@link Grasscutter}. */
|
||||
public final class BaseServerTest {
|
||||
@Getter private static int httpPort = -1;
|
||||
@Getter private static int gamePort = -1;
|
||||
|
||||
/**
|
||||
* Creates an HTTP URL.
|
||||
*
|
||||
* @param route The route to use.
|
||||
* @return The URL.
|
||||
*/
|
||||
public static String http(String route) {
|
||||
return "http://127.0.0.1:" + BaseServerTest.httpPort + "/" + route;
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void entry() {
|
||||
try {
|
||||
// Start Grasscutter.
|
||||
Grasscutter.main(new String[] {"-test"});
|
||||
} catch (Exception ignored) {
|
||||
throw new AssertException("Grasscutter failed to start.");
|
||||
}
|
||||
|
||||
// Set the ports.
|
||||
BaseServerTest.httpPort = Configuration.SERVER.http.bindPort;
|
||||
BaseServerTest.gamePort = Configuration.SERVER.game.bindPort;
|
||||
|
||||
// Create virtual instances.
|
||||
GrasscutterTest.account = new VirtualAccount();
|
||||
GrasscutterTest.gameSession = new VirtualGameSession();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("HTTP server check")
|
||||
public void checkHttpServer() {
|
||||
// Create a request.
|
||||
var request = new Request.Builder().url(BaseServerTest.http("")).build();
|
||||
|
||||
// Perform the request.
|
||||
try (var response = GrasscutterTest.httpClient.newCall(request).execute()) {
|
||||
// Check the response.
|
||||
Assertions.assertTrue(response.isSuccessful());
|
||||
} catch (IOException exception) {
|
||||
throw new AssertionError(exception);
|
||||
}
|
||||
}
|
||||
}
|
67
src/test/java/io/grasscutter/tests/LoginTest.java
Normal file
67
src/test/java/io/grasscutter/tests/LoginTest.java
Normal file
@ -0,0 +1,67 @@
|
||||
package io.grasscutter.tests;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.*;
|
||||
import emu.grasscutter.server.game.session.GameSessionManager;
|
||||
import io.grasscutter.*;
|
||||
import kcp.highway.*;
|
||||
import lombok.Getter;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public final class LoginTest {
|
||||
@Getter private static final Ukcp KCP = new Ukcp(
|
||||
null, null, null,
|
||||
new ChannelConfig(), null);
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@DisplayName("Connect to server")
|
||||
public void connectToServer() {
|
||||
var session = GrasscutterTest.getGameSession();
|
||||
|
||||
// Register the session.
|
||||
GameSessionManager.getSessions().put(KCP, session);
|
||||
|
||||
// Try connecting to the server.
|
||||
session.exchangeToken();
|
||||
Assertions.assertTrue(session.waitForPacket(
|
||||
PacketOpcodes.GetPlayerTokenRsp, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
@DisplayName("Login to server")
|
||||
public void loginToServer() {
|
||||
var account = GrasscutterTest.getAccount();
|
||||
var session = GrasscutterTest.getGameSession();
|
||||
|
||||
// Wait for the login response.
|
||||
TestUtils.waitFor(session::useSecretKey);
|
||||
|
||||
// Send the login packet.
|
||||
session.receive(
|
||||
PacketOpcodes.PlayerLoginReq,
|
||||
PlayerLoginReqOuterClass.PlayerLoginReq.newBuilder()
|
||||
.setToken(account.getToken())
|
||||
.build()
|
||||
);
|
||||
// Wait for the login response.
|
||||
Assertions.assertTrue(session.waitForPacket(
|
||||
PacketOpcodes.PlayerLoginRsp, 5));
|
||||
|
||||
// Send the born data request.
|
||||
session.receive(
|
||||
PacketOpcodes.SetPlayerBornDataReq,
|
||||
SetPlayerBornDataReqOuterClass.SetPlayerBornDataReq.newBuilder()
|
||||
.setAvatarId(GameConstants.MAIN_CHARACTER_FEMALE)
|
||||
.setNickName("Virtual Player")
|
||||
.build()
|
||||
);
|
||||
// Wait for the born data response.
|
||||
Assertions.assertTrue(session.waitForPacket(
|
||||
PacketOpcodes.SetPlayerBornDataRsp, 5));
|
||||
}
|
||||
}
|
23
src/test/java/io/grasscutter/virtual/VirtualAccount.java
Normal file
23
src/test/java/io/grasscutter/virtual/VirtualAccount.java
Normal file
@ -0,0 +1,23 @@
|
||||
package io.grasscutter.virtual;
|
||||
|
||||
import emu.grasscutter.game.Account;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class VirtualAccount extends Account {
|
||||
public VirtualAccount() {
|
||||
super();
|
||||
|
||||
this.setId("virtual_account");
|
||||
this.setUsername("virtual_account");
|
||||
this.setPassword("virtual_account");
|
||||
|
||||
this.setReservedPlayerUid(10001);
|
||||
this.setEmail("virtual_account@grasscutter.io");
|
||||
this.setLocale(Locale.US);
|
||||
|
||||
this.generateSessionKey();
|
||||
this.generateLoginToken();
|
||||
}
|
||||
}
|
142
src/test/java/io/grasscutter/virtual/VirtualGameSession.java
Normal file
142
src/test/java/io/grasscutter/virtual/VirtualGameSession.java
Normal file
@ -0,0 +1,142 @@
|
||||
package io.grasscutter.virtual;
|
||||
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.GetPlayerTokenReqOuterClass.GetPlayerTokenReq;
|
||||
import emu.grasscutter.net.proto.PacketHeadOuterClass.PacketHead;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.utils.Crypto;
|
||||
import io.grasscutter.GrasscutterTest;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.slf4j.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class VirtualGameSession extends GameSession {
|
||||
private static final Logger logger = LoggerFactory.getLogger("Game Session");
|
||||
|
||||
private final Map<Integer, Set<Function<byte[], Boolean>>> listeners = new HashMap<>();
|
||||
|
||||
public VirtualGameSession() {
|
||||
super(Grasscutter.getGameServer());
|
||||
|
||||
this.setAccount(GrasscutterTest.getAccount());
|
||||
this.onConnected(new VirtualKcpTunnel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an exchange with the server for the player's token.
|
||||
*/
|
||||
public void exchangeToken() {
|
||||
var account = GrasscutterTest.getAccount();
|
||||
|
||||
this.receive(
|
||||
PacketOpcodes.GetPlayerTokenReq,
|
||||
GetPlayerTokenReq.newBuilder()
|
||||
.setAccountUid(account.getId())
|
||||
.setAccountToken(account.getToken())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener for a packet.
|
||||
*
|
||||
* @param packetId The packet's ID.
|
||||
* @param listener The listener to register.
|
||||
*/
|
||||
public void addPacketListener(int packetId, Function<byte[], Boolean> listener) {
|
||||
var listeners = this.listeners.computeIfAbsent(
|
||||
packetId, k -> new HashSet<>());
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a packet to be received.
|
||||
*
|
||||
* @param packetId The packet's ID.
|
||||
* @param timeout The timeout in milliseconds.
|
||||
*/
|
||||
public boolean waitForPacket(int packetId, int timeout) {
|
||||
var promise = new CompletableFuture<byte[]>();
|
||||
this.addPacketListener(packetId, data -> {
|
||||
promise.complete(data);
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
promise.get(timeout, TimeUnit.SECONDS);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setPlayer(Player player) {
|
||||
var newPlayer = new VirtualPlayer();
|
||||
|
||||
GrasscutterTest.setPlayer(newPlayer);
|
||||
super.setPlayer(newPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a packet from the client.
|
||||
*
|
||||
* @param packetId The packet's ID.
|
||||
* @param message The packet to receive.
|
||||
*/
|
||||
public void receive(int packetId, GeneratedMessageV3 message) {
|
||||
// Craft a packet header.
|
||||
var header = PacketHead.newBuilder()
|
||||
.setSentMs(System.currentTimeMillis())
|
||||
.build();
|
||||
// Serialize the message.
|
||||
var headerBytes = header.toByteArray();
|
||||
var messageBytes = message.toByteArray();
|
||||
|
||||
// Wrap the message into a packet.
|
||||
var packet = Unpooled.buffer(12);
|
||||
packet.writeShort(17767); // Packet header.
|
||||
packet.writeShort(packetId); // Packet "opcode" or ID.
|
||||
packet.writeShort(headerBytes.length); // Packet head length.
|
||||
packet.writeInt(messageBytes.length); // Packet body length.
|
||||
packet.writeBytes(headerBytes); // Packet head.
|
||||
packet.writeBytes(messageBytes); // Packet body.
|
||||
packet.writeShort(-30293); // Packet footer.
|
||||
|
||||
// Serialize the packet.
|
||||
var data = packet.array();
|
||||
// Encrypt the packet if specified.
|
||||
Crypto.xor(data, this.useSecretKey() ?
|
||||
Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
|
||||
|
||||
// Dispatch the message to the server.
|
||||
GrasscutterTest.getExecutor()
|
||||
.submit(() -> this.onMessage(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(BasePacket packet) {
|
||||
// Invoke packet handlers.
|
||||
var listeners = this.listeners.get(packet.getOpcode());
|
||||
if (listeners != null) {
|
||||
var copy = new HashSet<>(listeners);
|
||||
for (var listener : copy) {
|
||||
if (listener.apply(packet.getData())) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log the received packet.
|
||||
logger.info("Received packet {} ({}) of length {} (header is {}).",
|
||||
PacketOpcodesUtils.getOpcodeName(packet.getOpcode()), packet.getOpcode(),
|
||||
packet.getData() == null ? "null" : packet.getData().length,
|
||||
packet.getHeader() == null ? "null" : packet.getHeader().length);
|
||||
}
|
||||
}
|
21
src/test/java/io/grasscutter/virtual/VirtualKcpTunnel.java
Normal file
21
src/test/java/io/grasscutter/virtual/VirtualKcpTunnel.java
Normal file
@ -0,0 +1,21 @@
|
||||
package io.grasscutter.virtual;
|
||||
|
||||
import emu.grasscutter.net.KcpTunnel;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public final class VirtualKcpTunnel implements KcpTunnel {
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return new InetSocketAddress(1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(byte[] bytes) {
|
||||
throw new UnsupportedOperationException("Cannot write to a virtual KCP tunnel");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
13
src/test/java/io/grasscutter/virtual/VirtualPlayer.java
Normal file
13
src/test/java/io/grasscutter/virtual/VirtualPlayer.java
Normal file
@ -0,0 +1,13 @@
|
||||
package io.grasscutter.virtual;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import io.grasscutter.GrasscutterTest;
|
||||
import io.grasscutter.tests.BaseServerTest;
|
||||
|
||||
public final class VirtualPlayer extends Player {
|
||||
public VirtualPlayer() {
|
||||
super(GrasscutterTest.getGameSession());
|
||||
|
||||
this.setAccount(GrasscutterTest.getAccount());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user