Finish testing and assembling a basic server and client structure
This commit is contained in:
parent
5c4a8c6a58
commit
3be0a9ab5f
5 changed files with 192 additions and 38 deletions
24
bin/client_test.dart
Normal file
24
bin/client_test.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:libac_flutter/nbt/Stream.dart';
|
||||||
|
import 'package:libac_flutter/nbt/Tag.dart';
|
||||||
|
import 'package:libac_flutter/nbt/impl/CompoundTag.dart';
|
||||||
|
import 'package:libac_flutter/packets/packets.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
PacketRegistry reg = PacketRegistry();
|
||||||
|
reg.registerDefaults();
|
||||||
|
|
||||||
|
PacketClient client = PacketClient();
|
||||||
|
await client.startConnect("127.0.0.1");
|
||||||
|
|
||||||
|
S2CResponse response = await client.send(C2SPing());
|
||||||
|
CompoundTag tag = response.contents;
|
||||||
|
StringBuilder builder = StringBuilder();
|
||||||
|
Tag.writeStringifiedNamedTag(tag, builder, 0);
|
||||||
|
|
||||||
|
print("Response from server: \n${builder}");
|
||||||
|
|
||||||
|
await client.send(StopServerPacket());
|
||||||
|
|
||||||
|
if (client.connected) await client.close();
|
||||||
|
return;
|
||||||
|
}
|
5
bin/server_test.dart
Normal file
5
bin/server_test.dart
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import 'package:libac_flutter/packets/packets.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
await PacketServer.start();
|
||||||
|
}
|
7
compile.sh
Executable file
7
compile.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mkdir out
|
||||||
|
rm -rf out/*
|
||||||
|
flutter pub publish -f --skip-validation
|
||||||
|
dart compile exe -o out/server_test bin/server_test.dart
|
||||||
|
dart compile exe -o out/client_test bin/client_test.dart
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
@ -6,8 +7,11 @@ import 'package:libac_flutter/nbt/Tag.dart';
|
||||||
import 'package:libac_flutter/nbt/impl/CompoundTag.dart';
|
import 'package:libac_flutter/nbt/impl/CompoundTag.dart';
|
||||||
import 'package:libac_flutter/nbt/impl/StringTag.dart';
|
import 'package:libac_flutter/nbt/impl/StringTag.dart';
|
||||||
|
|
||||||
|
import '../nbt/Stream.dart';
|
||||||
|
|
||||||
class PacketServer {
|
class PacketServer {
|
||||||
static ServerSocket? socket;
|
static ServerSocket? socket;
|
||||||
|
static bool shouldRestart = true;
|
||||||
static Future<void> start() async {
|
static Future<void> start() async {
|
||||||
socket = await ServerSocket.bind(InternetAddress.anyIPv4, 25306);
|
socket = await ServerSocket.bind(InternetAddress.anyIPv4, 25306);
|
||||||
print("Server now listening on port 25306");
|
print("Server now listening on port 25306");
|
||||||
|
@ -18,24 +22,38 @@ class PacketServer {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sock.listen((data) async {
|
sock.listen((data) async {
|
||||||
S2CStatusResponse response = S2CStatusResponse();
|
S2CResponse response = S2CResponse();
|
||||||
try {
|
try {
|
||||||
CompoundTag tag = await NbtIo.readFromStream(data);
|
CompoundTag tag = await NbtIo.readFromStream(data);
|
||||||
|
StringBuilder builder = StringBuilder();
|
||||||
|
Tag.writeStringifiedNamedTag(tag, builder, 0);
|
||||||
|
|
||||||
|
print("Request from client: \n${builder}");
|
||||||
|
|
||||||
C2SRequestPacket request = C2SRequestPacket();
|
C2SRequestPacket request = C2SRequestPacket();
|
||||||
request.decodeTag(tag);
|
request.decodeTag(tag);
|
||||||
|
|
||||||
PacketResponse reply = await request.handlePacket();
|
PacketResponse reply = await request.handleServerPacket();
|
||||||
// Server uses NBT to communicate
|
// Server uses NBT to communicate
|
||||||
sock.add(await NbtIo.writeToStream(reply.replyDataTag));
|
sock.add(await NbtIo.writeToStream(reply.replyDataTag));
|
||||||
} catch (E, stack) {
|
} catch (E, stack) {
|
||||||
response.reason = "Malformed request packet";
|
response.contents
|
||||||
|
.put("error", StringTag.valueOf("Malformed request packet"));
|
||||||
|
|
||||||
sock.add(
|
sock.add(
|
||||||
await NbtIo.writeToStream(response.encodeTag() as CompoundTag));
|
await NbtIo.writeToStream(response.encodeTag() as CompoundTag));
|
||||||
|
} finally {
|
||||||
|
await sock.flush();
|
||||||
|
sock.close();
|
||||||
|
|
||||||
|
if (!shouldRestart) {
|
||||||
|
await socket!.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, onDone: () {
|
}, onDone: () {
|
||||||
sock.close();
|
sock.close();
|
||||||
}, onError: () {
|
}, onError: (E) {
|
||||||
|
print("ERROR: ${E}");
|
||||||
sock.close();
|
sock.close();
|
||||||
});
|
});
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
|
@ -48,36 +66,53 @@ class PacketServer {
|
||||||
class PacketClient {
|
class PacketClient {
|
||||||
Socket? socket;
|
Socket? socket;
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
|
String lastIP = "";
|
||||||
|
|
||||||
PacketClient(String IPAddress);
|
PacketClient();
|
||||||
|
|
||||||
Future<void> startConnect(String IPAddress) async {
|
Future<void> startConnect(String IPAddress) async {
|
||||||
try {
|
try {
|
||||||
socket = await Socket.connect(IPAddress, 25306);
|
socket = await Socket.connect(IPAddress, 25306);
|
||||||
connected = true;
|
connected = true;
|
||||||
|
lastIP = IPAddress;
|
||||||
} catch (E, stack) {
|
} catch (E, stack) {
|
||||||
connected = false;
|
connected = false;
|
||||||
socket = null;
|
socket = null;
|
||||||
|
|
||||||
print(stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CompoundTag> send(IPacket packet) async {
|
Future<S2CResponse> send(IPacket packet) async {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
return CompoundTag();
|
return S2CResponse();
|
||||||
}
|
}
|
||||||
socket!.add(await NbtIo.writeToStream(packet.encodeTag().asCompoundTag()));
|
C2SRequestPacket request = C2SRequestPacket();
|
||||||
|
request.payload = packet;
|
||||||
|
request.cap = packet.getChannelID();
|
||||||
|
|
||||||
|
socket!.add(await NbtIo.writeToStream(request.encodeTag().asCompoundTag()));
|
||||||
CompoundTag ct = CompoundTag();
|
CompoundTag ct = CompoundTag();
|
||||||
|
Completer<void> onCompletion = Completer();
|
||||||
socket!.listen((data) async {
|
socket!.listen((data) async {
|
||||||
ct = await NbtIo.readFromStream(data);
|
CompoundTag result = await NbtIo.readFromStream(data);
|
||||||
|
ct.put("result", result);
|
||||||
|
}, onError: (E) {
|
||||||
|
print("ERROR: ${E}");
|
||||||
|
}, onDone: () {
|
||||||
|
print("Request completed");
|
||||||
|
onCompletion.complete();
|
||||||
});
|
});
|
||||||
|
|
||||||
return ct;
|
await onCompletion.future;
|
||||||
|
await close();
|
||||||
|
await startConnect(lastIP);
|
||||||
|
S2CResponse reply = S2CResponse();
|
||||||
|
reply.decodeTag(ct.get("result")!.asCompoundTag());
|
||||||
|
|
||||||
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
Future<void> close() async {
|
||||||
socket!.close();
|
await socket!.close();
|
||||||
connected = false;
|
connected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +121,8 @@ abstract class IPacket with NbtEncodable, JsonEncodable {
|
||||||
String getChannelID();
|
String getChannelID();
|
||||||
|
|
||||||
// This function handles the packet
|
// This function handles the packet
|
||||||
Future<PacketResponse> handlePacket();
|
Future<PacketResponse> handleServerPacket();
|
||||||
|
Future<void> handleClientPacket();
|
||||||
NetworkDirection direction();
|
NetworkDirection direction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,16 +157,24 @@ class StopServerPacket extends IPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PacketResponse> handlePacket() async {
|
Future<PacketResponse> handleServerPacket() async {
|
||||||
PacketServer.socket!.close();
|
// We're now on the server. Handle the packet with a response to the client
|
||||||
|
PacketServer.shouldRestart = false;
|
||||||
|
|
||||||
return PacketResponse(replyDataTag: CompoundTag());
|
S2CResponse response = S2CResponse();
|
||||||
|
|
||||||
|
return PacketResponse(replyDataTag: response.encodeTag().asCompoundTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleClientPacket() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PacketResponse {
|
class PacketResponse {
|
||||||
|
@ -145,7 +189,9 @@ class PacketRegistry {
|
||||||
Map<String, IPacket Function()> _registry = {};
|
Map<String, IPacket Function()> _registry = {};
|
||||||
static PacketRegistry _inst = PacketRegistry._();
|
static PacketRegistry _inst = PacketRegistry._();
|
||||||
|
|
||||||
PacketRegistry._();
|
PacketRegistry._() {
|
||||||
|
registerDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
factory PacketRegistry() {
|
factory PacketRegistry() {
|
||||||
return _inst;
|
return _inst;
|
||||||
|
@ -166,8 +212,8 @@ class PacketRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerDefaults() {
|
void registerDefaults() {
|
||||||
register(S2CStatusResponse(), () {
|
register(S2CResponse(), () {
|
||||||
return S2CStatusResponse();
|
return S2CResponse();
|
||||||
});
|
});
|
||||||
register(C2SRequestPacket(), () {
|
register(C2SRequestPacket(), () {
|
||||||
return C2SRequestPacket();
|
return C2SRequestPacket();
|
||||||
|
@ -175,6 +221,9 @@ class PacketRegistry {
|
||||||
register(StopServerPacket(), () {
|
register(StopServerPacket(), () {
|
||||||
return StopServerPacket();
|
return StopServerPacket();
|
||||||
});
|
});
|
||||||
|
register(C2SPing(), () {
|
||||||
|
return C2SPing();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +231,8 @@ enum NetworkDirection { ClientToServer, ServerToClient }
|
||||||
|
|
||||||
enum PacketOperation { Encode, Decode }
|
enum PacketOperation { Encode, Decode }
|
||||||
|
|
||||||
class S2CStatusResponse implements IPacket {
|
class S2CResponse implements IPacket {
|
||||||
String reason = "";
|
CompoundTag contents = CompoundTag();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NetworkDirection direction() {
|
NetworkDirection direction() {
|
||||||
|
@ -192,12 +241,12 @@ class S2CStatusResponse implements IPacket {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getChannelID() {
|
String getChannelID() {
|
||||||
return "StatusResponse";
|
return "Response";
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PacketResponse> handlePacket() async {
|
Future<PacketResponse> handleServerPacket() async {
|
||||||
// No handling is required for this packet type
|
// We can't predict handling for this type, it is a data packet response with no pre-defined structure.
|
||||||
return PacketResponse.nil;
|
return PacketResponse.nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +258,7 @@ class S2CStatusResponse implements IPacket {
|
||||||
@override
|
@override
|
||||||
void decodeTag(Tag encoded) {
|
void decodeTag(Tag encoded) {
|
||||||
CompoundTag ct = encoded as CompoundTag;
|
CompoundTag ct = encoded as CompoundTag;
|
||||||
reason = ct.get("reason")!.asString();
|
contents = ct.get("contents")!.asCompoundTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -220,23 +269,22 @@ class S2CStatusResponse implements IPacket {
|
||||||
@override
|
@override
|
||||||
Tag encodeTag() {
|
Tag encodeTag() {
|
||||||
CompoundTag tag = CompoundTag();
|
CompoundTag tag = CompoundTag();
|
||||||
tag.put("reason", StringTag.valueOf(reason));
|
tag.put("contents", contents);
|
||||||
|
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void fromJson(Map<String, dynamic> params) {
|
void fromJson(Map<String, dynamic> params) {}
|
||||||
reason = params["reason"] as String;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
Map<String, dynamic> map = {
|
return {}; // Operation is not supported at this time.
|
||||||
"reason": reason,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return map;
|
@override
|
||||||
|
Future<void> handleClientPacket() async {
|
||||||
|
// We haven't got anything to process. This is structured data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,15 +337,85 @@ class C2SRequestPacket implements IPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PacketResponse> handlePacket() async {
|
Future<PacketResponse> handleServerPacket() async {
|
||||||
// This has no internal handling
|
// This has no internal handling
|
||||||
return payload.handlePacket();
|
return payload.handleServerPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {"cap": payload.getChannelID(), "payload": payload.toJson()};
|
return {"cap": payload.getChannelID(), "payload": payload.toJson()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleClientPacket() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2SPing implements IPacket {
|
||||||
|
String clientVersion = "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void decodeJson(String params) {
|
||||||
|
fromJson(json.decode(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void decodeTag(Tag tag) {
|
||||||
|
clientVersion = tag.asCompoundTag().get("version")!.asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NetworkDirection direction() {
|
||||||
|
return NetworkDirection.ClientToServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String encodeJson() {
|
||||||
|
return json.encode(toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Tag encodeTag() {
|
||||||
|
CompoundTag tag = CompoundTag();
|
||||||
|
tag.put("version", StringTag.valueOf(clientVersion));
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void fromJson(Map<String, dynamic> js) {
|
||||||
|
clientVersion = js['version'] as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getChannelID() {
|
||||||
|
return "Ping";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PacketResponse> handleServerPacket() async {
|
||||||
|
CompoundTag tag = CompoundTag();
|
||||||
|
tag.put("pong", StringTag.valueOf(Platform.version));
|
||||||
|
|
||||||
|
S2CResponse response = S2CResponse();
|
||||||
|
response.contents = tag;
|
||||||
|
|
||||||
|
PacketResponse reply =
|
||||||
|
PacketResponse(replyDataTag: response.encodeTag().asCompoundTag());
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {"version": clientVersion};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleClientPacket() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin JsonEncodable {
|
mixin JsonEncodable {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: libac_flutter
|
name: libac_flutter
|
||||||
description: "Aria's Creations code library"
|
description: "Aria's Creations code library"
|
||||||
version: 1.0.11
|
version: 1.0.12
|
||||||
homepage: "https://zontreck.com"
|
homepage: "https://zontreck.com"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue