Add a network packet system
This commit is contained in:
parent
71f028f25c
commit
a082210fa9
4 changed files with 266 additions and 1 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
238
lib/packets/packets.dart
Normal 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);
|
||||
}
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue