ConanServerManager/lib/packets/ClientPackets.dart

335 lines
8.3 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:libac_dart/nbt/NbtUtils.dart';
import 'package:libac_dart/nbt/Stream.dart';
import 'package:libac_dart/nbt/Tag.dart';
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
import 'package:libac_dart/nbt/impl/StringTag.dart';
import 'package:libac_dart/packets/packets.dart';
import 'package:libac_dart/utils/Hashing.dart';
import 'package:libac_dart/utils/uuid/NbtUUID.dart';
import 'package:libac_dart/utils/uuid/UUID.dart';
import 'package:servermanager/statemachine.dart';
import 'package:servermanager/structs/SessionData.dart';
import 'package:servermanager/structs/discordHookHelper.dart';
import 'package:servermanager/structs/settings.dart';
class ClientPackets {
static void register() {
PacketRegistry reg = PacketRegistry();
reg.register(C2SLoginPacket(), () {
return C2SLoginPacket();
});
reg.register(S2CLoginReply(), () {
return S2CLoginReply();
});
reg.register(C2SRequestSettingsPacket(), () {
return C2SRequestSettingsPacket();
});
reg.register(C2SUploadSettingsPacket(), () {
return C2SUploadSettingsPacket();
});
}
}
class C2SLoginPacket implements IPacket {
String username = "";
String password = "";
@override
void decodeJson(String params) {
fromJson(json.decode(params));
}
@override
void decodeTag(Tag tag) {
CompoundTag ct = tag.asCompoundTag();
username = ct.get("username")!.asString();
password = ct.get("password")!.asString();
}
@override
NetworkDirection direction() {
return NetworkDirection.ClientToServer;
}
@override
String encodeJson() {
return json.encode(toJson());
}
@override
Tag encodeTag() {
CompoundTag tag = CompoundTag();
tag.put("username", StringTag.valueOf(username));
tag.put("password", StringTag.valueOf(Hashing.sha256Hash(password)));
return tag;
}
@override
void fromJson(Map<String, dynamic> js) {
username = js['username'] as String;
password = js['password'] as String;
}
@override
String getChannelID() {
return "Login";
}
@override
Future<PacketResponse> handleServerPacket() async {
S2CResponse response = S2CResponse();
S2CLoginReply loginReply = S2CLoginReply();
// Attempt to log in.
Settings settings = Settings();
if (settings.serverLoginCreds.username == username &&
Hashing.sha256Hash(settings.serverLoginCreds.password) == password) {
settings.remoteLoginToken = UUID.generate(4);
loginReply.valid = true;
loginReply.token = settings.remoteLoginToken;
} else {
//print(
// "Login failure\n${settings.serverLoginCreds.username}:${username}\n${Hashing.sha256Hash(settings.serverLoginCreds.password)}:${password}");
loginReply.valid = false;
}
response.contents = loginReply.encodeTag().asCompoundTag();
return PacketResponse(replyDataTag: response.encodeTag().asCompoundTag());
}
@override
Map<String, dynamic> toJson() {
return {"username": username, "password": Hashing.sha256Hash(password)};
}
@override
Future<void> handleClientPacket() async {}
}
class S2CLoginReply implements IPacket {
bool valid = false;
UUID token = UUID.ZERO;
@override
void decodeJson(String params) {
fromJson(json.decode(params));
}
@override
void decodeTag(Tag tag) {
print("Decoding S2C LoginReply");
StringBuilder sb = StringBuilder();
Tag.writeStringifiedNamedTag(tag, sb, 0);
print(sb);
CompoundTag ct = tag as CompoundTag;
if (ct.containsKey("valid")) valid = NbtUtils.readBoolean(ct, "valid");
if (ct.containsKey("token")) {
token = NbtUtils.readUUID(ct, "token").toUUID();
}
}
@override
NetworkDirection direction() {
return NetworkDirection.ServerToClient;
}
@override
String encodeJson() {
return json.encode(toJson());
}
@override
Tag encodeTag() {
CompoundTag tag = CompoundTag();
NbtUtils.writeBoolean(tag, "valid", valid);
NbtUtils.writeUUID(tag, "token", NbtUUID.fromUUID(token));
return tag;
}
@override
void fromJson(Map<String, dynamic> js) {
valid = js['valid'] as bool;
token = UUID.parse(js['token'] as String);
}
@override
String getChannelID() {
return "LoginReply";
}
@override
Future<void> handleClientPacket() async {
// Handle login finalization related stuff
Settings settings = Settings();
settings.remoteLoginToken = token;
}
@override
Future<PacketResponse> handleServerPacket() async {
return PacketResponse.nil; // We only operate on the client
}
@override
Map<String, dynamic> toJson() {
return {"valid": valid, "token": token.toString()};
}
}
class C2SRequestSettingsPacket implements IPacket {
CompoundTag serverSettings = CompoundTag();
@override
void decodeJson(String params) {
throw UnsupportedError("Json is unsupported by LibACNBT at this time");
}
@override
void decodeTag(Tag tag) {
CompoundTag ct = tag as CompoundTag;
serverSettings = ct.get("settings")!.asCompoundTag();
}
@override
NetworkDirection direction() {
return NetworkDirection.ClientToServer;
}
@override
String encodeJson() {
return "";
}
@override
Tag encodeTag() {
CompoundTag ct = CompoundTag();
ct.put("settings", Settings().serialize());
return ct;
}
@override
void fromJson(Map<String, dynamic> js) {}
@override
String getChannelID() {
return "C2SRequestSettings";
}
@override
Future<void> handleClientPacket() async {
Settings settings = Settings();
settings.deserialize(serverSettings);
settings.server = false;
}
@override
Future<PacketResponse> handleServerPacket() async {
S2CResponse response = S2CResponse();
Settings settings = Settings();
serverSettings = settings.serialize();
response.contents = encodeTag().asCompoundTag();
return PacketResponse(replyDataTag: response.encodeTag().asCompoundTag());
}
@override
Map<String, dynamic> toJson() {
return {};
}
}
class C2SUploadSettingsPacket implements IPacket {
CompoundTag srvSettings = CompoundTag();
bool performRestart = false;
@override
void decodeJson(String params) {}
@override
void decodeTag(Tag tag) {
srvSettings = tag.asCompoundTag().get("settings")!.asCompoundTag();
}
@override
NetworkDirection direction() {
return NetworkDirection.ClientToServer;
}
@override
String encodeJson() {
return "";
}
@override
Tag encodeTag() {
CompoundTag tag = CompoundTag();
tag.put("settings", Settings().serialize());
NbtUtils.writeBoolean(tag, "restart", performRestart);
return tag;
}
@override
void fromJson(Map<String, dynamic> js) {}
@override
String getChannelID() {
return "C2SUploadSettings";
}
@override
Future<void> handleClientPacket() async {
// No client response or handling needed
}
@override
Future<PacketResponse> handleServerPacket() async {
Settings settings = Settings();
CompoundTag currentSettings = settings.serialize();
try {
settings.deserialize(srvSettings);
settings.Write();
if(!performRestart){
DiscordHookHelper.sendWebHook(settings.inst!.discord, DiscordHookProps.ONLINE_ALERT, "Server Wrapper Settings", "Server wrapper settings have been updated.\n\n${performRestart ? "A restart has been requested" : "A restart is not needed"}");
return PacketResponse.nil;
}
// Check if server is running, if not, stop immediately
// If server is running, schedule restart for 1 minute and send a alert to all players, then perform stop or restart depending on if running in Pterodactyl Compatibility mode
SessionData.shutdownMessage = "Server wrapper restart";
SessionData.timer.apply(60);
SessionData.CURRENT_INTERVAL = WarnIntervals.NONE;
if (settings.subsys.currentState == States.Inactive) {
Timer.periodic(Duration(seconds: 10), (timer) {
SessionData.shutdownPending = true;
// Stop packet server
PacketServer.socket!.close();
timer.cancel();
exit(0);
}); // We give time to allow the server to shut down gracefully.
}
return PacketResponse.nil;
} catch (E) {
settings.deserialize(currentSettings);
return PacketResponse.nil;
}
}
@override
Map<String, dynamic> toJson() {
return {};
}
}