Add a network packet system

This commit is contained in:
zontreck 2024-05-22 14:22:08 -07:00
parent 71f028f25c
commit a082210fa9
4 changed files with 266 additions and 1 deletions

View file

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:libac_flutter/nbt/Stream.dart';
import 'package:libac_flutter/nbt/Tag.dart';
import 'package:libac_flutter/nbt/impl/CompoundTag.dart';
@ -68,4 +70,22 @@ class NbtIo {
_io.resetPosition();
return Tag.readNamedTag(_io) as CompoundTag;
}
static Future<Uint8List> writeToStream(CompoundTag tag) async {
_io = ByteLayer();
Tag.writeNamedTag(tag, _io);
_io.compress();
return _io.bytes;
}
static Future<CompoundTag> readFromStream(Uint8List list) async {
_io = ByteLayer();
_io.writeBytes(list);
_io.resetPosition();
_io.decompress();
_io.resetPosition();
return Tag.readNamedTag(_io) as CompoundTag;
}
}

View file

@ -264,6 +264,13 @@ abstract class Tag {
}
}
CompoundTag asCompoundTag() {
if (this is CompoundTag) {
return this as CompoundTag;
} else
return CompoundTag();
}
void prettyPrint(int indent, bool recurse);
static String getCanonicalName(TagType type) {

238
lib/packets/packets.dart Normal file
View file

@ -0,0 +1,238 @@
import 'dart:convert';
import 'dart:io';
import 'package:libac_flutter/nbt/NbtIo.dart';
import 'package:libac_flutter/nbt/NbtUtils.dart';
import 'package:libac_flutter/nbt/Tag.dart';
import 'package:libac_flutter/nbt/impl/CompoundTag.dart';
import 'package:libac_flutter/nbt/impl/StringTag.dart';
class PacketServer {
static Future<void> start() async {
final ServerSocket socket =
await ServerSocket.bind(InternetAddress.anyIPv4, 25306);
print("Server now listening on port 25306");
await for (var sock in socket) {
print(
"New connection from ${sock.remoteAddress.address}:${sock.remotePort}");
sock.listen((data) async {
S2CStatusResponse response = S2CStatusResponse();
try {
CompoundTag tag = await NbtIo.readFromStream(data);
} catch (E) {
response.status = false;
response.reason = "Malformed request packet";
sock.write(
await NbtIo.writeToStream(response.encodeTag() as CompoundTag));
}
}, onDone: () {
sock.close();
}, onError: () {
sock.close();
});
}
}
}
abstract class IPacket with NbtEncodable, JsonEncodable {
String getChannelID();
// This function handles the packet
Future<void> handlePacket();
NetworkDirection direction();
}
class PacketRegistry {
Map<String, IPacket Function()> _registry = {};
static PacketRegistry _inst = PacketRegistry._();
PacketRegistry._();
factory PacketRegistry() {
return _inst;
}
void register(IPacket packet, IPacket Function() packetResolver) {
_registry[packet.getChannelID()] = packetResolver;
}
IPacket getPacket(String channel) {
if (_registry.containsKey(channel)) {
IPacket Function() callback = _registry[channel]!;
return callback();
} else
throw Exception("No such channel has been registered");
}
void registerDefaults() {
register(S2CStatusResponse(), () {
return S2CStatusResponse();
});
}
}
enum NetworkDirection { ClientToServer, ServerToClient }
enum PacketOperation { Encode, Decode }
class S2CStatusResponse implements IPacket {
bool status = false;
String reason = "";
late IPacket payload;
@override
NetworkDirection direction() {
return NetworkDirection.ServerToClient;
}
@override
String getChannelID() {
return "StatusResponse";
}
@override
Future<void> handlePacket() async {
// No handling is required for this packet type
if (status) {
payload.handlePacket();
}
}
@override
void decodeJson(String encoded) {
fromJson(json.decode(encoded));
}
@override
void decodeTag(Tag encoded) {
CompoundTag ct = encoded as CompoundTag;
status = NbtUtils.readBoolean(ct, "status");
reason = ct.get("reason")!.asString();
if (ct.contains("payload")) {
String channel = ct.get("pc")!.asString();
payload = PacketRegistry().getPacket(channel);
payload.decodeTag(ct.get("payload")!);
}
}
@override
String encodeJson() {
return json.encode(toJson());
}
@override
Tag encodeTag() {
CompoundTag tag = CompoundTag();
NbtUtils.writeBoolean(tag, "status", status);
tag.put("reason", StringTag.valueOf(reason));
if (status) {
tag.put("pc", StringTag.valueOf(payload.getChannelID()));
tag.put("payload", payload.encodeTag());
}
return tag;
}
@override
void fromJson(Map<String, dynamic> params) {
status = params["status"] as bool;
reason = params["reason"] as String;
if (status) {
String channel = params['pc'] as String;
payload = PacketRegistry().getPacket(channel);
payload.fromJson(params['payload']);
}
}
@override
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {
"status": status,
"reason": reason,
};
if (status) {
map.addAll({"pc": payload.getChannelID(), "payload": payload.toJson()});
}
return map;
}
}
class C2SRequestPacket implements IPacket {
String cap = ""; // Packet channel
late IPacket payload;
@override
void decodeJson(String encoded) {
fromJson(json.decode(encoded));
}
@override
void decodeTag(Tag encoded) {
CompoundTag tag = encoded.asCompoundTag();
String cap = tag.get("cap")!.asString();
payload = PacketRegistry().getPacket(cap);
payload.decodeTag(tag.get("payload")!.asCompoundTag());
}
@override
NetworkDirection direction() {
return NetworkDirection.ClientToServer;
}
@override
String encodeJson() {
return json.encode(toJson());
}
@override
Tag encodeTag() {
CompoundTag tag = CompoundTag();
tag.put("cap", StringTag.valueOf(payload.getChannelID()));
tag.put("payload", payload.encodeTag());
return tag;
}
@override
void fromJson(Map<String, dynamic> params) {
String cap = params['cap'] as String;
payload = PacketRegistry().getPacket(cap);
payload.fromJson(params['payload']);
}
@override
String getChannelID() {
return "C2SRequest";
}
@override
Future<void> handlePacket() async {
// This has no internal handling
}
@override
Map<String, dynamic> toJson() {
return {"cap": payload.getChannelID(), "payload": payload.toJson()};
}
}
mixin JsonEncodable {
String encodeJson();
void decodeJson(String params);
Map<String, dynamic> toJson();
void fromJson(Map<String, dynamic> js);
}
mixin NbtEncodable {
Tag encodeTag();
void decodeTag(Tag tag);
}

View file

@ -1,6 +1,6 @@
name: libac_flutter
description: "Aria's Creations code library"
version: 1.0.7
version: 1.0.8
homepage: "https://zontreck.com"
environment: