refactor(server): typed protobuf wire + sealed record dispatcher scaffolding

Phase 01+02a of the WebSocket protobuf migration:
- Add request.proto / response.proto with typed oneofs (14 request + 20 response variants)
- Wire com.google.protobuf Gradle plugin v0.9.6 (protoc 3.25.5)
- Drop TCP stack: ProtobufProxy, Proxy, ProtobufTransferHandler, SecondProtobufCodec
- Delete old envelope types ClientTransferData, ServerTransferData
- Delete reflection-based ServerEventListener + 13 ServerEventListener_CODE_* classes
- Create sealed ClientRequest interface + 14 record variants
- Create RequestConverter (wire -> record) and RequestDispatcher (record -> handler)
- Rewrite ChannelUtils.push(Channel, Response) for BinaryWebSocketFrame
- Rewrite WebsocketTransferHandler for BinaryWebSocketFrame + typed dispatch
- Flip default port 1024 -> 1999; SimpleServer starts only WebsocketProxy
- Move RoomClearTask scheduling into WebsocketProxy.start()
- Bump netty 4.1.115.Final -> 4.1.128.Final, junit-bom 5.11.3 -> 5.11.4, shadow 8.3.5 -> 8.3.8
- Heartbeat handled as no-op in dispatcher; all other cases throw until phase 02b

Phase 02b will port the 14 business-logic handlers and wire the dispatcher.
This commit is contained in:
2026-04-11 08:10:16 +07:00
parent 687cab7863
commit 945a249b89
49 changed files with 449 additions and 2880 deletions
+11 -4
View File
@@ -1,11 +1,12 @@
plugins {
java
id("com.gradleup.shadow") version "8.3.5"
id("com.google.protobuf") version "0.9.6"
id("com.gradleup.shadow") version "8.3.8"
}
group = "com.miti99.caro"
version = "0.0.1"
description = "Caro (Gomoku) multiplayer game server - Netty-based TCP + WebSocket"
description = "Caro (Gomoku) multiplayer game server - Netty WebSocket / Protobuf"
java {
toolchain {
@@ -18,15 +19,21 @@ repositories {
}
dependencies {
implementation("io.netty:netty-all:4.1.115.Final")
implementation("io.netty:netty-all:4.1.128.Final")
implementation("com.google.protobuf:protobuf-java:3.25.5")
implementation("com.google.code.gson:gson:2.11.0")
testImplementation(platform("org.junit:junit-bom:5.11.3"))
testImplementation(platform("org.junit:junit-bom:5.11.4"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.5"
}
}
tasks.compileJava {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
@@ -1,57 +1,27 @@
package com.miti99.caro.common.channel;
import com.miti99.caro.common.entity.ClientTransferData;
import com.miti99.caro.common.entity.Msg;
import com.miti99.caro.common.entity.ServerTransferData;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.common.utils.JsonUtils;
import com.miti99.caro.protocol.Response;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
public class ChannelUtils {
/**
* Serialises a typed {@link Response} proto to a {@link BinaryWebSocketFrame}
* and writes it to the given channel. The single push point for all outbound
* server-to-client traffic.
*/
public final class ChannelUtils {
public static void pushToClient(Channel channel, ClientEventCode code, String data) {
pushToClient(channel, code, data, null);
}
public static void pushToClient(Channel channel, ClientEventCode code, String data, String info) {
if (channel != null) {
if (channel.pipeline().get("ws") != null) {
var msg = new Msg(code.toString(), data, info);
channel.writeAndFlush(new TextWebSocketFrame(JsonUtils.toJson(msg)));
} else {
var clientTransferData = ClientTransferData.ClientTransferDataProtoc.newBuilder();
if (code != null) {
clientTransferData.setCode(code.toString());
}
if (data != null) {
clientTransferData.setData(data);
}
if (info != null) {
clientTransferData.setInfo(info);
}
channel.writeAndFlush(clientTransferData);
}
}
}
public static ChannelFuture pushToServer(Channel channel, ServerEventCode code, String data) {
if (channel.pipeline().get("ws") != null) {
var msg = new Msg(code.toString(), data, null);
return channel.writeAndFlush(new TextWebSocketFrame(JsonUtils.toJson(msg)));
} else {
var serverTransferData = ServerTransferData.ServerTransferDataProtoc.newBuilder();
if (code != null) {
serverTransferData.setCode(code.toString());
}
if (data != null) {
serverTransferData.setData(data);
}
return channel.writeAndFlush(serverTransferData);
}
}
private ChannelUtils() {
}
public static ChannelFuture push(Channel channel, Response response) {
if (channel == null) {
return null;
}
byte[] bytes = response.toByteArray();
return channel.writeAndFlush(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(bytes)));
}
}
@@ -1,882 +0,0 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: ClientTransferDataProtoc.proto
package com.miti99.caro.common.entity;
public final class ClientTransferData {
private ClientTransferData() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface ClientTransferDataProtocOrBuilder extends
// @@protoc_insertion_point(interface_extends:com.miti99.caro.common.entity.ClientTransferDataProtoc)
com.google.protobuf.MessageOrBuilder {
/**
* <code>string code = 1;</code>
*/
java.lang.String getCode();
/**
* <code>string code = 1;</code>
*/
com.google.protobuf.ByteString
getCodeBytes();
/**
* <code>string data = 2;</code>
*/
java.lang.String getData();
/**
* <code>string data = 2;</code>
*/
com.google.protobuf.ByteString
getDataBytes();
/**
* <code>string info = 3;</code>
*/
java.lang.String getInfo();
/**
* <code>string info = 3;</code>
*/
com.google.protobuf.ByteString
getInfoBytes();
}
/**
* Protobuf type {@code com.miti99.caro.common.entity.ClientTransferDataProtoc}
*/
public static final class ClientTransferDataProtoc extends
com.google.protobuf.GeneratedMessageV3 implements
// @@protoc_insertion_point(message_implements:com.miti99.caro.common.entity.ClientTransferDataProtoc)
ClientTransferDataProtocOrBuilder {
private static final long serialVersionUID = 0L;
// Use ClientTransferDataProtoc.newBuilder() to construct.
private ClientTransferDataProtoc(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
}
private ClientTransferDataProtoc() {
code_ = "";
data_ = "";
info_ = "";
}
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet
getUnknownFields() {
return this.unknownFields;
}
private ClientTransferDataProtoc(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
this();
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
case 10: {
java.lang.String s = input.readStringRequireUtf8();
code_ = s;
break;
}
case 18: {
java.lang.String s = input.readStringRequireUtf8();
data_ = s;
break;
}
case 26: {
java.lang.String s = input.readStringRequireUtf8();
info_ = s;
break;
}
default: {
if (!parseUnknownFieldProto3(
input, unknownFields, extensionRegistry, tag)) {
done = true;
}
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(
e).setUnfinishedMessage(this);
} finally {
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.miti99.caro.common.entity.ClientTransferData.internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.miti99.caro.common.entity.ClientTransferData.internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.class, com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.Builder.class);
}
public static final int CODE_FIELD_NUMBER = 1;
private volatile java.lang.Object code_;
/**
* <code>string code = 1;</code>
*/
public java.lang.String getCode() {
java.lang.Object ref = code_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
code_ = s;
return s;
}
}
/**
* <code>string code = 1;</code>
*/
public com.google.protobuf.ByteString
getCodeBytes() {
java.lang.Object ref = code_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
code_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
public static final int DATA_FIELD_NUMBER = 2;
private volatile java.lang.Object data_;
/**
* <code>string data = 2;</code>
*/
public java.lang.String getData() {
java.lang.Object ref = data_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
data_ = s;
return s;
}
}
/**
* <code>string data = 2;</code>
*/
public com.google.protobuf.ByteString
getDataBytes() {
java.lang.Object ref = data_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
data_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
public static final int INFO_FIELD_NUMBER = 3;
private volatile java.lang.Object info_;
/**
* <code>string info = 3;</code>
*/
public java.lang.String getInfo() {
java.lang.Object ref = info_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
info_ = s;
return s;
}
}
/**
* <code>string info = 3;</code>
*/
public com.google.protobuf.ByteString
getInfoBytes() {
java.lang.Object ref = info_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
info_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
private byte memoizedIsInitialized = -1;
@java.lang.Override
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized == 1) return true;
if (isInitialized == 0) return false;
memoizedIsInitialized = 1;
return true;
}
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (!getCodeBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 1, code_);
}
if (!getDataBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 2, data_);
}
if (!getInfoBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 3, info_);
}
unknownFields.writeTo(output);
}
@java.lang.Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
size = 0;
if (!getCodeBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, code_);
}
if (!getDataBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, data_);
}
if (!getInfoBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, info_);
}
size += unknownFields.getSerializedSize();
memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc)) {
return super.equals(obj);
}
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc other = (com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc) obj;
boolean result = true;
result = result && getCode()
.equals(other.getCode());
result = result && getData()
.equals(other.getData());
result = result && getInfo()
.equals(other.getInfo());
result = result && unknownFields.equals(other.unknownFields);
return result;
}
@java.lang.Override
public int hashCode() {
if (memoizedHashCode != 0) {
return memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + CODE_FIELD_NUMBER;
hash = (53 * hash) + getCode().hashCode();
hash = (37 * hash) + DATA_FIELD_NUMBER;
hash = (53 * hash) + getData().hashCode();
hash = (37 * hash) + INFO_FIELD_NUMBER;
hash = (53 * hash) + getInfo().hashCode();
hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash;
return hash;
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input, extensionRegistry);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input);
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input, extensionRegistry);
}
@java.lang.Override
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
@java.lang.Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE
? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
/**
* Protobuf type {@code com.miti99.caro.common.entity.ClientTransferDataProtoc}
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:com.miti99.caro.common.entity.ClientTransferDataProtoc)
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtocOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.miti99.caro.common.entity.ClientTransferData.internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.miti99.caro.common.entity.ClientTransferData.internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.class, com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.Builder.class);
}
// Construct using com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessageV3
.alwaysUseFieldBuilders) {
}
}
@java.lang.Override
public Builder clear() {
super.clear();
code_ = "";
data_ = "";
info_ = "";
return this;
}
@java.lang.Override
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return com.miti99.caro.common.entity.ClientTransferData.internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_descriptor;
}
@java.lang.Override
public com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc getDefaultInstanceForType() {
return com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.getDefaultInstance();
}
@java.lang.Override
public com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc build() {
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@java.lang.Override
public com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc buildPartial() {
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc result = new com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc(this);
result.code_ = code_;
result.data_ = data_;
result.info_ = info_;
onBuilt();
return result;
}
@java.lang.Override
public Builder clone() {
return (Builder) super.clone();
}
@java.lang.Override
public Builder setField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return (Builder) super.setField(field, value);
}
@java.lang.Override
public Builder clearField(
com.google.protobuf.Descriptors.FieldDescriptor field) {
return (Builder) super.clearField(field);
}
@java.lang.Override
public Builder clearOneof(
com.google.protobuf.Descriptors.OneofDescriptor oneof) {
return (Builder) super.clearOneof(oneof);
}
@java.lang.Override
public Builder setRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
int index, java.lang.Object value) {
return (Builder) super.setRepeatedField(field, index, value);
}
@java.lang.Override
public Builder addRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return (Builder) super.addRepeatedField(field, value);
}
@java.lang.Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc) {
return mergeFrom((com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc)other);
} else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc other) {
if (other == com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc.getDefaultInstance()) return this;
if (!other.getCode().isEmpty()) {
code_ = other.code_;
onChanged();
}
if (!other.getData().isEmpty()) {
data_ = other.data_;
onChanged();
}
if (!other.getInfo().isEmpty()) {
info_ = other.info_;
onChanged();
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
return this;
}
@java.lang.Override
public final boolean isInitialized() {
return true;
}
@java.lang.Override
public Builder mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc parsedMessage = null;
try {
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
parsedMessage = (com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc) e.getUnfinishedMessage();
throw e.unwrapIOException();
} finally {
if (parsedMessage != null) {
mergeFrom(parsedMessage);
}
}
return this;
}
private java.lang.Object code_ = "";
/**
* <code>string code = 1;</code>
*/
public java.lang.String getCode() {
java.lang.Object ref = code_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
code_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string code = 1;</code>
*/
public com.google.protobuf.ByteString
getCodeBytes() {
java.lang.Object ref = code_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
code_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string code = 1;</code>
*/
public Builder setCode(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
code_ = value;
onChanged();
return this;
}
/**
* <code>string code = 1;</code>
*/
public Builder clearCode() {
code_ = getDefaultInstance().getCode();
onChanged();
return this;
}
/**
* <code>string code = 1;</code>
*/
public Builder setCodeBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
code_ = value;
onChanged();
return this;
}
private java.lang.Object data_ = "";
/**
* <code>string data = 2;</code>
*/
public java.lang.String getData() {
java.lang.Object ref = data_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
data_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string data = 2;</code>
*/
public com.google.protobuf.ByteString
getDataBytes() {
java.lang.Object ref = data_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
data_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string data = 2;</code>
*/
public Builder setData(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
data_ = value;
onChanged();
return this;
}
/**
* <code>string data = 2;</code>
*/
public Builder clearData() {
data_ = getDefaultInstance().getData();
onChanged();
return this;
}
/**
* <code>string data = 2;</code>
*/
public Builder setDataBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
data_ = value;
onChanged();
return this;
}
private java.lang.Object info_ = "";
/**
* <code>string info = 3;</code>
*/
public java.lang.String getInfo() {
java.lang.Object ref = info_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
info_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string info = 3;</code>
*/
public com.google.protobuf.ByteString
getInfoBytes() {
java.lang.Object ref = info_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
info_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string info = 3;</code>
*/
public Builder setInfo(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
info_ = value;
onChanged();
return this;
}
/**
* <code>string info = 3;</code>
*/
public Builder clearInfo() {
info_ = getDefaultInstance().getInfo();
onChanged();
return this;
}
/**
* <code>string info = 3;</code>
*/
public Builder setInfoBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
info_ = value;
onChanged();
return this;
}
@java.lang.Override
public final Builder setUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.setUnknownFieldsProto3(unknownFields);
}
@java.lang.Override
public final Builder mergeUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.mergeUnknownFields(unknownFields);
}
// @@protoc_insertion_point(builder_scope:com.miti99.caro.common.entity.ClientTransferDataProtoc)
}
// @@protoc_insertion_point(class_scope:com.miti99.caro.common.entity.ClientTransferDataProtoc)
private static final com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc DEFAULT_INSTANCE;
static {
DEFAULT_INSTANCE = new com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc();
}
public static com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc getDefaultInstance() {
return DEFAULT_INSTANCE;
}
private static final com.google.protobuf.Parser<ClientTransferDataProtoc>
PARSER = new com.google.protobuf.AbstractParser<ClientTransferDataProtoc>() {
@java.lang.Override
public ClientTransferDataProtoc parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new ClientTransferDataProtoc(input, extensionRegistry);
}
};
public static com.google.protobuf.Parser<ClientTransferDataProtoc> parser() {
return PARSER;
}
@java.lang.Override
public com.google.protobuf.Parser<ClientTransferDataProtoc> getParserForType() {
return PARSER;
}
@java.lang.Override
public com.miti99.caro.common.entity.ClientTransferData.ClientTransferDataProtoc getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
}
private static final com.google.protobuf.Descriptors.Descriptor
internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_descriptor;
private static final
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\036ClientTransferDataProtoc.proto\022\037org.ni" +
"co.ratel.landlords.entity\"D\n\030ClientTrans" +
"ferDataProtoc\022\014\n\004code\030\001 \001(\t\022\014\n\004data\030\002 \001(" +
"\t\022\014\n\004info\030\003 \001(\tB5\n\037org.nico.ratel.landlo" +
"rds.entityB\022ClientTransferDatab\006proto3"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
}, assigner);
internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_org_nico_ratel_landlords_entity_ClientTransferDataProtoc_descriptor,
new java.lang.String[] { "Code", "Data", "Info", });
}
// @@protoc_insertion_point(outer_class_scope)
}
@@ -1,945 +0,0 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: ServerTransferDataProtoc.proto
package com.miti99.caro.common.entity;
public final class ServerTransferData {
private ServerTransferData() {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface ServerTransferDataProtocOrBuilder extends
// @@protoc_insertion_point(interface_extends:com.miti99.caro.common.entity.ServerTransferDataProtoc)
com.google.protobuf.MessageOrBuilder {
/**
* <code>string code = 1;</code>
*/
java.lang.String getCode();
/**
* <code>string code = 1;</code>
*/
com.google.protobuf.ByteString
getCodeBytes();
/**
* <code>string data = 2;</code>
*/
java.lang.String getData();
/**
* <code>string data = 2;</code>
*/
com.google.protobuf.ByteString
getDataBytes();
/**
* <code>string info = 3;</code>
*/
java.lang.String getInfo();
/**
* <code>string info = 3;</code>
*/
com.google.protobuf.ByteString
getInfoBytes();
}
/**
* Protobuf type {@code com.miti99.caro.common.entity.ServerTransferDataProtoc}
*/
public static final class ServerTransferDataProtoc extends
com.google.protobuf.GeneratedMessageV3 implements
// @@protoc_insertion_point(message_implements:com.miti99.caro.common.entity.ServerTransferDataProtoc)
ServerTransferDataProtocOrBuilder {
private static final long serialVersionUID = 0L;
// Use ServerTransferDataProtoc.newBuilder() to construct.
private ServerTransferDataProtoc(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
}
private ServerTransferDataProtoc() {
code_ = "";
data_ = "";
info_ = "";
}
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet
getUnknownFields() {
return this.unknownFields;
}
private ServerTransferDataProtoc(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
this();
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
case 10: {
java.lang.String s = input.readStringRequireUtf8();
code_ = s;
break;
}
case 18: {
java.lang.String s = input.readStringRequireUtf8();
data_ = s;
break;
}
case 26: {
java.lang.String s = input.readStringRequireUtf8();
info_ = s;
break;
}
default: {
if (!parseUnknownFieldProto3(
input, unknownFields, extensionRegistry, tag)) {
done = true;
}
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(
e).setUnfinishedMessage(this);
} finally {
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.miti99.caro.common.entity.ServerTransferData.internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.miti99.caro.common.entity.ServerTransferData.internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.class, com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.Builder.class);
}
public static final int CODE_FIELD_NUMBER = 1;
private volatile java.lang.Object code_;
/**
* <code>string code = 1;</code>
*/
public java.lang.String getCode() {
java.lang.Object ref = code_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
code_ = s;
return s;
}
}
/**
* <code>string code = 1;</code>
*/
public com.google.protobuf.ByteString
getCodeBytes() {
java.lang.Object ref = code_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
code_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
public static final int DATA_FIELD_NUMBER = 2;
private volatile java.lang.Object data_;
/**
* <code>string data = 2;</code>
*/
public java.lang.String getData() {
java.lang.Object ref = data_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
data_ = s;
return s;
}
}
/**
* <code>string data = 2;</code>
*/
public com.google.protobuf.ByteString
getDataBytes() {
java.lang.Object ref = data_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
data_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
public static final int INFO_FIELD_NUMBER = 3;
private volatile java.lang.Object info_;
/**
* <code>string info = 3;</code>
*/
public java.lang.String getInfo() {
java.lang.Object ref = info_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
info_ = s;
return s;
}
}
/**
* <code>string info = 3;</code>
*/
public com.google.protobuf.ByteString
getInfoBytes() {
java.lang.Object ref = info_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
info_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
private byte memoizedIsInitialized = -1;
@java.lang.Override
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized == 1) return true;
if (isInitialized == 0) return false;
memoizedIsInitialized = 1;
return true;
}
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (!getCodeBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 1, code_);
}
if (!getDataBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 2, data_);
}
if (!getInfoBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 3, info_);
}
unknownFields.writeTo(output);
}
@java.lang.Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
size = 0;
if (!getCodeBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, code_);
}
if (!getDataBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, data_);
}
if (!getInfoBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, info_);
}
size += unknownFields.getSerializedSize();
memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc)) {
return super.equals(obj);
}
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc other = (com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc) obj;
boolean result = true;
result = result && getCode()
.equals(other.getCode());
result = result && getData()
.equals(other.getData());
result = result && getInfo()
.equals(other.getInfo());
result = result && unknownFields.equals(other.unknownFields);
return result;
}
@java.lang.Override
public int hashCode() {
if (memoizedHashCode != 0) {
return memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + CODE_FIELD_NUMBER;
hash = (53 * hash) + getCode().hashCode();
hash = (37 * hash) + DATA_FIELD_NUMBER;
hash = (53 * hash) + getData().hashCode();
hash = (37 * hash) + INFO_FIELD_NUMBER;
hash = (53 * hash) + getInfo().hashCode();
hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash;
return hash;
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input, extensionRegistry);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input);
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input, extensionRegistry);
}
@java.lang.Override
public Builder newBuilderForType() {
return newBuilder();
}
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
@java.lang.Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE
? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
/**
* Protobuf type {@code com.miti99.caro.common.entity.ServerTransferDataProtoc}
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:com.miti99.caro.common.entity.ServerTransferDataProtoc)
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtocOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.miti99.caro.common.entity.ServerTransferData.internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.miti99.caro.common.entity.ServerTransferData.internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.class, com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.Builder.class);
}
// Construct using com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessageV3
.alwaysUseFieldBuilders) {
}
}
@java.lang.Override
public Builder clear() {
super.clear();
code_ = "";
data_ = "";
info_ = "";
return this;
}
@java.lang.Override
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return com.miti99.caro.common.entity.ServerTransferData.internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_descriptor;
}
@java.lang.Override
public com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc getDefaultInstanceForType() {
return com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.getDefaultInstance();
}
@java.lang.Override
public com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc build() {
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@java.lang.Override
public com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc buildPartial() {
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc result = new com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc(this);
result.code_ = code_;
result.data_ = data_;
result.info_ = info_;
onBuilt();
return result;
}
@java.lang.Override
public Builder clone() {
return (Builder) super.clone();
}
@java.lang.Override
public Builder setField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return (Builder) super.setField(field, value);
}
@java.lang.Override
public Builder clearField(
com.google.protobuf.Descriptors.FieldDescriptor field) {
return (Builder) super.clearField(field);
}
@java.lang.Override
public Builder clearOneof(
com.google.protobuf.Descriptors.OneofDescriptor oneof) {
return (Builder) super.clearOneof(oneof);
}
@java.lang.Override
public Builder setRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
int index, java.lang.Object value) {
return (Builder) super.setRepeatedField(field, index, value);
}
@java.lang.Override
public Builder addRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return (Builder) super.addRepeatedField(field, value);
}
@java.lang.Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc) {
return mergeFrom((com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc) other);
} else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc other) {
if (other == com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc.getDefaultInstance())
return this;
if (!other.getCode().isEmpty()) {
code_ = other.code_;
onChanged();
}
if (!other.getData().isEmpty()) {
data_ = other.data_;
onChanged();
}
if (!other.getInfo().isEmpty()) {
info_ = other.info_;
onChanged();
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
return this;
}
@java.lang.Override
public final boolean isInitialized() {
return true;
}
@java.lang.Override
public Builder mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc parsedMessage = null;
try {
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
parsedMessage = (com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc) e.getUnfinishedMessage();
throw e.unwrapIOException();
} finally {
if (parsedMessage != null) {
mergeFrom(parsedMessage);
}
}
return this;
}
private java.lang.Object code_ = "";
/**
* <code>string code = 1;</code>
*/
public java.lang.String getCode() {
java.lang.Object ref = code_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
code_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string code = 1;</code>
*/
public com.google.protobuf.ByteString
getCodeBytes() {
java.lang.Object ref = code_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
code_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string code = 1;</code>
*/
public Builder setCode(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
code_ = value;
onChanged();
return this;
}
/**
* <code>string code = 1;</code>
*/
public Builder clearCode() {
code_ = getDefaultInstance().getCode();
onChanged();
return this;
}
/**
* <code>string code = 1;</code>
*/
public Builder setCodeBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
code_ = value;
onChanged();
return this;
}
private java.lang.Object data_ = "";
/**
* <code>string data = 2;</code>
*/
public java.lang.String getData() {
java.lang.Object ref = data_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
data_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string data = 2;</code>
*/
public com.google.protobuf.ByteString
getDataBytes() {
java.lang.Object ref = data_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
data_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string data = 2;</code>
*/
public Builder setData(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
data_ = value;
onChanged();
return this;
}
/**
* <code>string data = 2;</code>
*/
public Builder clearData() {
data_ = getDefaultInstance().getData();
onChanged();
return this;
}
/**
* <code>string data = 2;</code>
*/
public Builder setDataBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
data_ = value;
onChanged();
return this;
}
private java.lang.Object info_ = "";
/**
* <code>string info = 3;</code>
*/
public java.lang.String getInfo() {
java.lang.Object ref = info_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
info_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string info = 3;</code>
*/
public com.google.protobuf.ByteString
getInfoBytes() {
java.lang.Object ref = info_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
info_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string info = 3;</code>
*/
public Builder setInfo(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
info_ = value;
onChanged();
return this;
}
/**
* <code>string info = 3;</code>
*/
public Builder clearInfo() {
info_ = getDefaultInstance().getInfo();
onChanged();
return this;
}
/**
* <code>string info = 3;</code>
*/
public Builder setInfoBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
info_ = value;
onChanged();
return this;
}
@java.lang.Override
public final Builder setUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.setUnknownFieldsProto3(unknownFields);
}
@java.lang.Override
public final Builder mergeUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.mergeUnknownFields(unknownFields);
}
// @@protoc_insertion_point(builder_scope:com.miti99.caro.common.entity.ServerTransferDataProtoc)
}
// @@protoc_insertion_point(class_scope:com.miti99.caro.common.entity.ServerTransferDataProtoc)
private static final com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc DEFAULT_INSTANCE;
static {
DEFAULT_INSTANCE = new com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc();
}
public static com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc getDefaultInstance() {
return DEFAULT_INSTANCE;
}
private static final com.google.protobuf.Parser<ServerTransferDataProtoc>
PARSER = new com.google.protobuf.AbstractParser<ServerTransferDataProtoc>() {
@java.lang.Override
public ServerTransferDataProtoc parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new ServerTransferDataProtoc(input, extensionRegistry);
}
};
public static com.google.protobuf.Parser<ServerTransferDataProtoc> parser() {
return PARSER;
}
@java.lang.Override
public com.google.protobuf.Parser<ServerTransferDataProtoc> getParserForType() {
return PARSER;
}
@java.lang.Override
public com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
}
private static final com.google.protobuf.Descriptors.Descriptor
internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_descriptor;
private static final
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\036ServerTransferDataProtoc.proto\022\037org.ni" +
"co.ratel.landlords.entity\"D\n\030ServerTrans" +
"ferDataProtoc\022\014\n\004code\030\001 \001(\t\022\014\n\004data\030\002 \001(" +
"\t\022\014\n\004info\030\003 \001(\tB5\n\037org.nico.ratel.landlo" +
"rds.entityB\022ServerTransferDatab\006proto3"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[]{
}, assigner);
internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_org_nico_ratel_landlords_entity_ServerTransferDataProtoc_descriptor,
new java.lang.String[]{"Code", "Data", "Info",});
}
// @@protoc_insertion_point(outer_class_scope)
}
@@ -16,7 +16,7 @@ public class ServerContains {
/**
* Server port
*/
public static int port = 1024;
public static int port = 1999;
/**
* The map of server side
@@ -1,24 +1,15 @@
package com.miti99.caro.server;
import com.miti99.caro.server.proxy.ProtobufProxy;
import com.miti99.caro.server.proxy.WebsocketProxy;
public class SimpleServer {
public static void main(String[] args) throws InterruptedException {
if (args != null && args.length > 1) {
if (args[0].equalsIgnoreCase("-p") || args[0].equalsIgnoreCase("-port")) {
ServerContains.port = Integer.parseInt(args[1]);
}
}
new Thread(() -> {
try {
new ProtobufProxy().start(ServerContains.port);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new WebsocketProxy().start(ServerContains.port + 1);
}
public static void main(String[] args) throws InterruptedException {
if (args != null && args.length > 1) {
if (args[0].equalsIgnoreCase("-p") || args[0].equalsIgnoreCase("-port")) {
ServerContains.port = Integer.parseInt(args[1]);
}
}
new WebsocketProxy().start(ServerContains.port);
}
}
@@ -0,0 +1,49 @@
package com.miti99.caro.server.event;
import com.miti99.caro.protocol.Request;
import com.miti99.caro.server.event.request.ClientExitRequestRecord;
import com.miti99.caro.server.event.request.ClientRequest;
import com.miti99.caro.server.event.request.CreatePveRoomRequestRecord;
import com.miti99.caro.server.event.request.CreateRoomRequestRecord;
import com.miti99.caro.server.event.request.GameMoveRequestRecord;
import com.miti99.caro.server.event.request.GameReadyRequestRecord;
import com.miti99.caro.server.event.request.GameResetRequestRecord;
import com.miti99.caro.server.event.request.GameStartingRequestRecord;
import com.miti99.caro.server.event.request.GetRoomsRequestRecord;
import com.miti99.caro.server.event.request.HeartbeatRequestRecord;
import com.miti99.caro.server.event.request.JoinRoomRequestRecord;
import com.miti99.caro.server.event.request.SetClientInfoRequestRecord;
import com.miti99.caro.server.event.request.SetNicknameRequestRecord;
import com.miti99.caro.server.event.request.WatchGameExitRequestRecord;
import com.miti99.caro.server.event.request.WatchGameRequestRecord;
/**
* Converts the wire-level protobuf {@link Request} into one of the sealed
* {@link ClientRequest} records. Exhaustive switch — the compiler enforces that
* every oneof variant is covered.
*/
public final class RequestConverter {
private RequestConverter() {
}
public static ClientRequest convert(Request req) {
return switch (req.getPayloadCase()) {
case HEARTBEAT -> new HeartbeatRequestRecord();
case SET_NICKNAME -> new SetNicknameRequestRecord(req.getSetNickname().getNickname());
case SET_CLIENT_INFO -> new SetClientInfoRequestRecord(req.getSetClientInfo().getVersion());
case CREATE_ROOM -> new CreateRoomRequestRecord();
case CREATE_PVE_ROOM -> new CreatePveRoomRequestRecord(req.getCreatePveRoom().getDifficulty());
case GET_ROOMS -> new GetRoomsRequestRecord();
case JOIN_ROOM -> new JoinRoomRequestRecord(req.getJoinRoom().getRoomId());
case GAME_STARTING -> new GameStartingRequestRecord();
case GAME_READY -> new GameReadyRequestRecord();
case GAME_MOVE -> new GameMoveRequestRecord(req.getGameMove().getRow(), req.getGameMove().getCol());
case GAME_RESET -> new GameResetRequestRecord();
case WATCH_GAME -> new WatchGameRequestRecord(req.getWatchGame().getRoomId());
case WATCH_GAME_EXIT -> new WatchGameExitRequestRecord();
case CLIENT_EXIT -> new ClientExitRequestRecord();
case PAYLOAD_NOT_SET -> throw new IllegalArgumentException("Request payload not set");
};
}
}
@@ -0,0 +1,56 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.server.event.request.ClientExitRequestRecord;
import com.miti99.caro.server.event.request.ClientRequest;
import com.miti99.caro.server.event.request.CreatePveRoomRequestRecord;
import com.miti99.caro.server.event.request.CreateRoomRequestRecord;
import com.miti99.caro.server.event.request.GameMoveRequestRecord;
import com.miti99.caro.server.event.request.GameReadyRequestRecord;
import com.miti99.caro.server.event.request.GameResetRequestRecord;
import com.miti99.caro.server.event.request.GameStartingRequestRecord;
import com.miti99.caro.server.event.request.GetRoomsRequestRecord;
import com.miti99.caro.server.event.request.HeartbeatRequestRecord;
import com.miti99.caro.server.event.request.JoinRoomRequestRecord;
import com.miti99.caro.server.event.request.SetClientInfoRequestRecord;
import com.miti99.caro.server.event.request.SetNicknameRequestRecord;
import com.miti99.caro.server.event.request.WatchGameExitRequestRecord;
import com.miti99.caro.server.event.request.WatchGameRequestRecord;
/**
* Routes a {@link ClientRequest} record to its handler via exhaustive pattern
* matching. Replaces the old reflection-based {@code ServerEventListener} lookup.
*
* <p>Phase 02a: only {@code HeartbeatRequestRecord} has a real no-op implementation;
* every other case throws {@link UnsupportedOperationException} pending Phase 02b.
*/
public final class RequestDispatcher {
private RequestDispatcher() {
}
public static void dispatch(ClientSide client, ClientRequest req) {
switch (req) {
case HeartbeatRequestRecord r -> {
/* no-op: heartbeat just refreshes IdleStateHandler */
}
case SetNicknameRequestRecord r -> throw todo("set_nickname");
case SetClientInfoRequestRecord r -> throw todo("set_client_info");
case CreateRoomRequestRecord r -> throw todo("create_room");
case CreatePveRoomRequestRecord r -> throw todo("create_pve_room");
case GetRoomsRequestRecord r -> throw todo("get_rooms");
case JoinRoomRequestRecord r -> throw todo("join_room");
case GameStartingRequestRecord r -> throw todo("game_starting");
case GameReadyRequestRecord r -> throw todo("game_ready");
case GameMoveRequestRecord r -> throw todo("game_move");
case GameResetRequestRecord r -> throw todo("game_reset");
case WatchGameRequestRecord r -> throw todo("watch_game");
case WatchGameExitRequestRecord r -> throw todo("watch_game_exit");
case ClientExitRequestRecord r -> throw todo("client_exit");
}
}
private static UnsupportedOperationException todo(String name) {
return new UnsupportedOperationException("TODO phase 02b: " + name);
}
}
@@ -1,41 +0,0 @@
package com.miti99.caro.server.event;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.enums.ServerEventCode;
public interface ServerEventListener {
void call(ClientSide client, String data);
Map<ServerEventCode, ServerEventListener> LISTENER_MAP = new HashMap<>();
String LISTENER_PREFIX = "com.miti99.caro.server.event.ServerEventListener_";
@SuppressWarnings("unchecked")
static ServerEventListener get(ServerEventCode code) {
ServerEventListener listener = null;
try {
if (ServerEventListener.LISTENER_MAP.containsKey(code)) {
listener = ServerEventListener.LISTENER_MAP.get(code);
} else {
String eventListener = LISTENER_PREFIX + code.name();
Class<ServerEventListener> listenerClass = (Class<ServerEventListener>) Class.forName(eventListener);
try {
listener = listenerClass.getDeclaredConstructor().newInstance();
} catch (InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
ServerEventListener.LISTENER_MAP.put(code, listener);
}
return listener;
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
@@ -1,43 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_CLIENT_EXIT implements ServerEventListener {
private static final Object locked = new Object();
@Override
public void call(ClientSide clientSide, String data) {
synchronized (locked) {
Room room = ServerContains.getRoom(clientSide.getRoomId());
if (room == null) {
return;
}
String result = MapHelper.newInstance()
.put("roomId", room.getId())
.put("exitClientId", clientSide.getId())
.put("exitClientNickname", clientSide.getNickname())
.json();
for (ClientSide client : room.getClientSideList()) {
if (client.getChannel() != null) {
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, result);
client.init();
}
}
// Notify spectators
for (ClientSide watcher : room.getWatcherList()) {
ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, clientSide.getNickname());
}
ServerContains.removeRoom(room.getId());
}
}
}
@@ -1,22 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.utils.JsonUtils;
import java.util.Map;
public class ServerEventListener_CODE_CLIENT_INFO_SET implements ServerEventListener {
private static final String DEFAULT_VERSION = "v1.2.8";
@Override
public void call(ClientSide client, String info) {
Map<?,?> infos = JsonUtils.fromJson(info, Map.class);
// Get client version
client.setVersion(DEFAULT_VERSION);
if (infos.containsKey("version")){
client.setVersion(String.valueOf(infos.get("version")));
}
}
}
@@ -1,24 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_CLIENT_NICKNAME_SET implements ServerEventListener {
public static final int NICKNAME_MAX_LENGTH = 10;
@Override
public void call(ClientSide client, String nickname) {
if (nickname.trim().length() > NICKNAME_MAX_LENGTH || nickname.trim().isEmpty()) {
String result = MapHelper.newInstance().put("invalidLength", nickname.trim().length()).json();
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_CLIENT_NICKNAME_SET, result);
return;
}
ServerContains.CLIENT_SIDE_MAP.get(client.getId()).setNickname(nickname);
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_SHOW_OPTIONS, null);
}
}
@@ -1,41 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_CLIENT_OFFLINE implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(clientSide.getRoomId());
if (room == null) {
ServerContains.CLIENT_SIDE_MAP.remove(clientSide.getId());
return;
}
if (room.getWatcherList().contains(clientSide)) {
room.getWatcherList().remove(clientSide);
return;
}
String result = MapHelper.newInstance()
.put("roomId", room.getId())
.put("exitClientId", clientSide.getId())
.put("exitClientNickname", clientSide.getNickname())
.json();
for (ClientSide client : room.getClientSideList()) {
if (client.getChannel() != null && client.getId() != clientSide.getId()) {
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_CLIENT_EXIT, result);
client.init();
}
}
ServerContains.removeRoom(room.getId());
}
}
@@ -1,147 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Board;
import com.miti99.caro.common.entity.GameMove;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.GameResult;
import com.miti99.caro.common.enums.PieceType;
import com.miti99.caro.common.enums.RoomType;
import com.miti99.caro.common.helper.GomokuHelper;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.common.robot.GomokuAI;
import com.miti99.caro.server.ServerContains;
import java.util.Map;
public class ServerEventListener_CODE_GAME_MOVE implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(clientSide.getRoomId());
if (room == null) {
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_PLAY_FAIL_BY_INEXIST, null);
return;
}
Map<String, Object> map = MapHelper.parser(data);
int row = (int) map.get("row");
int col = (int) map.get("col");
// Check turn
if (!room.isPlayerTurn(clientSide.getId())) {
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_MOVE_NOT_YOUR_TURN, null);
return;
}
Board board = room.getGameBoard();
// Check bounds
if (row < 0 || row >= Board.BOARD_SIZE || col < 0 || col >= Board.BOARD_SIZE) {
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_MOVE_OUT_OF_BOUNDS, null);
return;
}
// Check occupied
if (board.getPiece(row, col) != PieceType.EMPTY) {
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_MOVE_OCCUPIED, null);
return;
}
// Make the move
GameResult result = GomokuHelper.makeMove(room, row, col, clientSide.getId());
// Broadcast move to all players and spectators
String moveResult = MapHelper.newInstance()
.put("row", row)
.put("col", col)
.put("piece", room.getPlayerPiece(clientSide.getId()).name())
.put("playerNickname", clientSide.getNickname())
.put("playerId", clientSide.getId())
.json();
broadcastToRoom(room, ClientEventCode.CODE_GAME_MOVE_SUCCESS, moveResult);
// Check game over
if (result != GameResult.IN_PROGRESS) {
handleGameOver(room, result);
return;
}
// PVE: trigger AI move if next turn is AI
if (room.getType() == RoomType.PVE) {
handleAIMove(room);
}
}
private void handleAIMove(Room room) {
// Find AI player (the one with null channel)
ClientSide aiPlayer = null;
for (ClientSide client : room.getClientSideList()) {
if (client.getChannel() == null) {
aiPlayer = client;
break;
}
}
if (aiPlayer == null || !room.isPlayerTurn(aiPlayer.getId())) {
return;
}
PieceType aiPiece = room.getPlayerPiece(aiPlayer.getId());
GomokuAI ai = new GomokuAI(aiPiece);
GameMove aiMove = ai.getNextMove(room.getGameBoard(), room.getDifficultyCoefficient());
if (aiMove == null) {
return;
}
GameResult result = GomokuHelper.makeMove(room, aiMove.getRow(), aiMove.getCol(), aiPlayer.getId());
String moveResult = MapHelper.newInstance()
.put("row", aiMove.getRow())
.put("col", aiMove.getCol())
.put("piece", aiPiece.name())
.put("playerNickname", aiPlayer.getNickname())
.put("playerId", aiPlayer.getId())
.json();
broadcastToRoom(room, ClientEventCode.CODE_GAME_MOVE_SUCCESS, moveResult);
if (result != GameResult.IN_PROGRESS) {
handleGameOver(room, result);
}
}
private void handleGameOver(Room room, GameResult result) {
String winnerNickname = "";
if (result == GameResult.BLACK_WIN) {
ClientSide winner = room.getClientSideMap().get(room.getBlackPlayerId());
winnerNickname = winner != null ? winner.getNickname() : "Black";
} else if (result == GameResult.WHITE_WIN) {
ClientSide winner = room.getClientSideMap().get(room.getWhitePlayerId());
winnerNickname = winner != null ? winner.getNickname() : "White";
}
// NOTE: do not include the formatted board here — it contains literal
// newlines which break the client's nested JSON.parse, leaving `data`
// as a raw string so data.result becomes undefined and the client
// always shows "You Lose!". The web client re-renders from its own
// move history and doesn't need a serialized board.
String gameOverData = MapHelper.newInstance()
.put("result", result.name())
.put("winnerNickname", winnerNickname)
.json();
broadcastToRoom(room, ClientEventCode.CODE_GAME_OVER, gameOverData);
}
private void broadcastToRoom(Room room, ClientEventCode code, String data) {
for (ClientSide client : room.getClientSideList()) {
if (client.getChannel() != null) {
ChannelUtils.pushToClient(client.getChannel(), code, data);
}
}
for (ClientSide watcher : room.getWatcherList()) {
ChannelUtils.pushToClient(watcher.getChannel(), code, data);
}
}
}
@@ -1,53 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.RoomStatus;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_GAME_READY implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(clientSide.getRoomId());
if (room == null || room.getStatus() == RoomStatus.STARTING) {
return;
}
if (clientSide.getStatus() == ClientStatus.PLAYING) {
return;
}
// Toggle ready state
clientSide.setStatus(clientSide.getStatus() == ClientStatus.READY ? ClientStatus.NO_READY : ClientStatus.READY);
String result = MapHelper.newInstance()
.put("clientNickName", clientSide.getNickname())
.put("status", clientSide.getStatus())
.put("clientId", clientSide.getId())
.json();
// Check if all human players are ready (need 2 players)
boolean allReady = room.getClientSideMap().size() >= 2;
for (ClientSide client : room.getClientSideList()) {
if (client.getChannel() != null && client.getStatus() != ClientStatus.READY) {
allReady = false;
}
}
// Notify all human players
for (ClientSide client : room.getClientSideList()) {
if (client.getChannel() != null) {
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_GAME_READY, result);
}
}
if (allReady) {
ServerEventListener.get(ServerEventCode.CODE_GAME_STARTING).call(clientSide, data);
}
}
}
@@ -1,68 +0,0 @@
package com.miti99.caro.server.event;
import java.util.LinkedList;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientRole;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.PieceType;
import com.miti99.caro.common.enums.RoomStatus;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_GAME_STARTING implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(clientSide.getRoomId());
if (room == null) {
return;
}
LinkedList<ClientSide> roomClientList = room.getClientSideList();
if (roomClientList.size() < 2) {
return;
}
// Assign players: first = BLACK, second = WHITE
ClientSide blackPlayer = roomClientList.get(0);
ClientSide whitePlayer = roomClientList.get(1);
blackPlayer.setRole(ClientRole.BLACK_PLAYER);
whitePlayer.setRole(ClientRole.WHITE_PLAYER);
blackPlayer.setStatus(ClientStatus.PLAYING);
whitePlayer.setStatus(ClientStatus.PLAYING);
room.setBlackPlayerId(blackPlayer.getId());
room.setWhitePlayerId(whitePlayer.getId());
room.setCurrentTurn(PieceType.BLACK);
room.setStatus(RoomStatus.STARTING);
room.setCreateTime(System.currentTimeMillis());
room.setLastFlushTime(System.currentTimeMillis());
room.resetGame();
String result = MapHelper.newInstance()
.put("roomId", room.getId())
.put("blackPlayerId", blackPlayer.getId())
.put("blackPlayerNickname", blackPlayer.getNickname())
.put("whitePlayerId", whitePlayer.getId())
.put("whitePlayerNickname", whitePlayer.getNickname())
.put("boardSize", 15)
.json();
// Notify human players
for (ClientSide client : roomClientList) {
if (client.getChannel() != null) {
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_GAME_STARTING, result);
}
}
// Notify spectators
for (ClientSide watcher : room.getWatcherList()) {
ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_GAME_STARTING, result);
}
}
}
@@ -1,37 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.common.utils.JsonUtils;
import java.util.HashMap;
import java.util.Map;
public class ServerEventListener_CODE_GAME_WATCH implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(Integer.parseInt(data));
if (room == null) {
String result = MapHelper.newInstance()
.put("roomId", data)
.json();
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_JOIN_FAIL_BY_INEXIST, result);
} else {
// Add user to the room's spectator list
clientSide.setRoomId(room.getId());
room.getWatcherList().add(clientSide);
Map<String, String> map = new HashMap<>(16);
map.put("owner", room.getRoomOwner());
map.put("status", room.getStatus().toString());
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_GAME_WATCH_SUCCESSFUL, JsonUtils.toJson(map));
}
}
}
@@ -1,23 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_GAME_WATCH_EXIT implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(clientSide.getRoomId());
if (room != null) {
// Remove spectator from room's watcher list if room exists
clientSide.setRoomId(room.getId());
boolean successful = room.getWatcherList().remove(clientSide);
if (successful) {
SimplePrinter.serverLog(clientSide.getNickname() + " exit room " + room.getId());
}
}
}
}
@@ -1,33 +0,0 @@
package com.miti99.caro.server.event;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.common.utils.JsonUtils;
public class ServerEventListener_CODE_GET_ROOMS implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
List<Map<String, Object>> roomList = new ArrayList<>(ServerContains.getRoomMap().size());
for (Entry<Integer, Room> entry : ServerContains.getRoomMap().entrySet()) {
Room room = entry.getValue();
roomList.add(MapHelper.newInstance()
.put("roomId", room.getId())
.put("roomOwner", room.getRoomOwner())
.put("roomClientCount", room.getClientSideList().size())
.put("roomType", room.getType())
.map());
}
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_SHOW_ROOMS, JsonUtils.toJson(roomList));
}
}
@@ -1,35 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientRole;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.RoomStatus;
import com.miti99.caro.common.enums.RoomType;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.common.utils.JsonUtils;
public class ServerEventListener_CODE_ROOM_CREATE implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = new Room(ServerContains.getServerId());
room.setStatus(RoomStatus.WAIT);
room.setType(RoomType.PVP);
room.setRoomOwner(clientSide.getNickname());
room.getClientSideMap().put(clientSide.getId(), clientSide);
room.getClientSideList().add(clientSide);
room.setCreateTime(System.currentTimeMillis());
room.setLastFlushTime(System.currentTimeMillis());
clientSide.setRoomId(room.getId());
clientSide.setRole(ClientRole.BLACK_PLAYER);
clientSide.setStatus(ClientStatus.NO_READY);
ServerContains.addRoom(room);
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_CREATE_SUCCESS, JsonUtils.toJson(room));
}
}
@@ -1,60 +0,0 @@
package com.miti99.caro.server.event;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientRole;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.RoomStatus;
import com.miti99.caro.common.enums.RoomType;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_ROOM_CREATE_PVE implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
int difficulty = Integer.parseInt(data);
if (difficulty < 1 || difficulty > 3) {
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_PVE_DIFFICULTY_NOT_SUPPORT, null);
return;
}
Room room = new Room(ServerContains.getServerId());
room.setType(RoomType.PVE);
room.setStatus(RoomStatus.WAIT);
room.setRoomOwner(clientSide.getNickname());
room.setDifficultyCoefficient(difficulty);
room.setCreateTime(System.currentTimeMillis());
room.setLastFlushTime(System.currentTimeMillis());
// Add human player
room.getClientSideMap().put(clientSide.getId(), clientSide);
room.getClientSideList().add(clientSide);
clientSide.setRoomId(room.getId());
clientSide.setRole(ClientRole.BLACK_PLAYER);
// Add AI robot (1 robot for 2-player Gomoku)
ClientSide robot = new ClientSide(-ServerContains.getClientId(), ClientStatus.PLAYING, null);
robot.setNickname("AI_" + getDifficultyName(difficulty));
robot.setRole(ClientRole.WHITE_PLAYER);
robot.setRoomId(room.getId());
room.getClientSideMap().put(robot.getId(), robot);
room.getClientSideList().add(robot);
ServerContains.CLIENT_SIDE_MAP.put(robot.getId(), robot);
ServerContains.addRoom(room);
// Auto-start game
ServerEventListener.get(ServerEventCode.CODE_GAME_STARTING).call(clientSide, String.valueOf(room.getId()));
}
private String getDifficultyName(int difficulty) {
return switch (difficulty) {
case 2 -> "Medium";
case 3 -> "Hard";
default -> "Easy";
};
}
}
@@ -1,72 +0,0 @@
package com.miti99.caro.server.event;
import java.util.LinkedList;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.RoomStatus;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.common.helper.MapHelper;
import com.miti99.caro.server.ServerContains;
public class ServerEventListener_CODE_ROOM_JOIN implements ServerEventListener {
@Override
public void call(ClientSide clientSide, String data) {
Room room = ServerContains.getRoom(Integer.parseInt(data));
if (room == null) {
String result = MapHelper.newInstance()
.put("roomId", data)
.json();
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_JOIN_FAIL_BY_INEXIST, result);
return;
}
// Gomoku is 2-player
if (room.getClientSideList().size() >= 2) {
String result = MapHelper.newInstance()
.put("roomId", room.getId())
.put("roomOwner", room.getRoomOwner())
.json();
ChannelUtils.pushToClient(clientSide.getChannel(), ClientEventCode.CODE_ROOM_JOIN_FAIL_BY_FULL, result);
return;
}
clientSide.setStatus(ClientStatus.READY);
clientSide.setRoomId(room.getId());
LinkedList<ClientSide> roomClientList = room.getClientSideList();
roomClientList.add(clientSide);
room.getClientSideMap().put(clientSide.getId(), clientSide);
room.setStatus(RoomStatus.WAIT);
String result = MapHelper.newInstance()
.put("clientId", clientSide.getId())
.put("clientNickname", clientSide.getNickname())
.put("roomId", room.getId())
.put("roomOwner", room.getRoomOwner())
.put("roomClientCount", roomClientList.size())
.json();
for (ClientSide client : room.getClientSideMap().values()) {
if (client.getChannel() != null) {
ChannelUtils.pushToClient(client.getChannel(), ClientEventCode.CODE_ROOM_JOIN_SUCCESS, result);
}
}
// Auto-start when 2 players joined
if (roomClientList.size() == 2) {
ServerEventListener.get(ServerEventCode.CODE_GAME_STARTING).call(clientSide, String.valueOf(room.getId()));
return;
}
// Notify spectators
for (ClientSide watcher : room.getWatcherList()) {
ChannelUtils.pushToClient(watcher.getChannel(), ClientEventCode.CODE_ROOM_JOIN_SUCCESS, clientSide.getNickname());
}
}
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record ClientExitRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,23 @@
package com.miti99.caro.server.event.request;
/**
* Sealed taxonomy of every client-to-server request. One record per oneof variant
* of {@code miti99.caro.protocol.Request}. Exhaustive pattern matching in
* {@code com.miti99.caro.server.event.RequestDispatcher}.
*/
public sealed interface ClientRequest
permits HeartbeatRequestRecord,
SetNicknameRequestRecord,
SetClientInfoRequestRecord,
CreateRoomRequestRecord,
CreatePveRoomRequestRecord,
GetRoomsRequestRecord,
JoinRoomRequestRecord,
GameStartingRequestRecord,
GameReadyRequestRecord,
GameMoveRequestRecord,
GameResetRequestRecord,
WatchGameRequestRecord,
WatchGameExitRequestRecord,
ClientExitRequestRecord {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record CreatePveRoomRequestRecord(int difficulty) implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record CreateRoomRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record GameMoveRequestRecord(int row, int col) implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record GameReadyRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record GameResetRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record GameStartingRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record GetRoomsRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record HeartbeatRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record JoinRoomRequestRecord(int roomId) implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record SetClientInfoRequestRecord(String version) implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record SetNicknameRequestRecord(String nickname) implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record WatchGameExitRequestRecord() implements ClientRequest {
}
@@ -0,0 +1,4 @@
package com.miti99.caro.server.event.request;
public record WatchGameRequestRecord(int roomId) implements ClientRequest {
}
@@ -1,94 +0,0 @@
package com.miti99.caro.server.handler;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientRole;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.event.ServerEventListener;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
public class ProtobufTransferHandler extends ChannelInboundHandlerAdapter {
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
ClientSide client = ServerContains.CLIENT_SIDE_MAP.get(getId(ctx.channel()));
SimplePrinter.serverLog("client " + client.getId() + "(" + client.getNickname() + ") disconnected");
clientOfflineEvent(ctx.channel());
ctx.channel().close();
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
Channel ch = ctx.channel();
//init client info
ClientSide clientSide = new ClientSide(getId(ctx.channel()), ClientStatus.TO_CHOOSE, ch);
clientSide.setNickname(String.valueOf(clientSide.getId()));
clientSide.setRole(ClientRole.BLACK_PLAYER);
ServerContains.CLIENT_SIDE_MAP.put(clientSide.getId(), clientSide);
SimplePrinter.serverLog("Has client connect to the server: " + clientSide.getId());
ChannelUtils.pushToClient(ch, ClientEventCode.CODE_CLIENT_CONNECT, String.valueOf(clientSide.getId()));
ChannelUtils.pushToClient(ch, ClientEventCode.CODE_CLIENT_NICKNAME_SET, null);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ServerTransferDataProtoc) {
ServerTransferDataProtoc serverTransferData = (ServerTransferDataProtoc) msg;
ServerEventCode code = ServerEventCode.valueOf(serverTransferData.getCode());
if (code != ServerEventCode.CODE_CLIENT_HEAD_BEAT) {
ClientSide client = ServerContains.CLIENT_SIDE_MAP.get(getId(ctx.channel()));
SimplePrinter.serverLog(client.getId() + " | " + client.getNickname() + " do:" + code.getMsg());
ServerEventListener.get(code).call(client, serverTransferData.getData());
}
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
try {
clientOfflineEvent(ctx.channel());
ctx.channel().close();
} catch (Exception ignore) {
}
}
} else {
super.userEventTriggered(ctx, evt);
}
}
private int getId(Channel channel) {
String longId = channel.id().asLongText();
Integer clientId = ServerContains.CHANNEL_ID_MAP.get(longId);
if (null == clientId) {
clientId = ServerContains.getClientId();
ServerContains.CHANNEL_ID_MAP.put(longId, clientId);
}
return clientId;
}
private void clientOfflineEvent(Channel channel) {
int clientId = getId(channel);
ClientSide client = ServerContains.CLIENT_SIDE_MAP.get(clientId);
if (client != null) {
SimplePrinter.serverLog("Has client exit to the server" + clientId + " | " + client.getNickname());
ServerEventListener.get(ServerEventCode.CODE_CLIENT_OFFLINE).call(client, null);
}
}
}
@@ -1,25 +0,0 @@
package com.miti99.caro.server.handler;
import java.util.List;
import com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc;
import com.google.protobuf.MessageLite;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
public class SecondProtobufCodec extends MessageToMessageCodec<ServerTransferDataProtoc, MessageLite> {
@Override
protected void encode(ChannelHandlerContext ctx, MessageLite msg, List<Object> out) {
out.add(msg);
}
@Override
protected void decode(ChannelHandlerContext ctx, ServerTransferDataProtoc msg, List<Object> out) {
out.add(msg);
}
}
@@ -1,39 +1,49 @@
package com.miti99.caro.server.handler;
import com.google.protobuf.InvalidProtocolBufferException;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.enums.ClientRole;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.protocol.ClientConnectResponse;
import com.miti99.caro.protocol.NicknameSetResponse;
import com.miti99.caro.protocol.Request;
import com.miti99.caro.protocol.Response;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.event.RequestConverter;
import com.miti99.caro.server.event.RequestDispatcher;
import com.miti99.caro.server.event.request.ClientRequest;
import com.miti99.caro.server.event.request.HeartbeatRequestRecord;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import com.miti99.caro.common.channel.ChannelUtils;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Msg;
import com.miti99.caro.common.entity.ServerTransferData.ServerTransferDataProtoc;
import com.miti99.caro.common.enums.ClientEventCode;
import com.miti99.caro.common.enums.ClientRole;
import com.miti99.caro.common.enums.ClientStatus;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.event.ServerEventListener;
import com.miti99.caro.common.utils.JsonUtils;
import java.util.Objects;
public class WebsocketTransferHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
public class WebsocketTransferHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
var msg = JsonUtils.fromJson(frame.text(), Msg.class);
var code = ServerEventCode.valueOf(msg.code());
if (!Objects.equals(code, ServerEventCode.CODE_CLIENT_HEAD_BEAT)) {
var client = ServerContains.CLIENT_SIDE_MAP.get(getId(ctx.channel()));
SimplePrinter.serverLog(client.getId() + " | " + client.getNickname() + " do:" + code.getMsg());
ServerEventListener.get(code).call(client, msg.data());
protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame frame) {
byte[] bytes = ByteBufUtil.getBytes(frame.content());
Request raw;
try {
raw = Request.parseFrom(bytes);
} catch (InvalidProtocolBufferException e) {
SimplePrinter.serverLog("WARN malformed request: " + e.getMessage());
return;
}
ClientRequest req = RequestConverter.convert(raw);
ClientSide client = ServerContains.CLIENT_SIDE_MAP.get(getId(ctx.channel()));
if (!(req instanceof HeartbeatRequestRecord)) {
SimplePrinter.serverLog(
client.getId() + " | " + client.getNickname() + " do: " + req.getClass().getSimpleName());
}
RequestDispatcher.dispatch(client, req);
}
@Override
@@ -41,20 +51,19 @@ public class WebsocketTransferHandler extends SimpleChannelInboundHandler<TextWe
if (cause instanceof java.io.IOException) {
clientOfflineEvent(ctx.channel());
} else {
SimplePrinter.serverLog("ERROR" + cause.getMessage());
SimplePrinter.serverLog("ERROR: " + cause.getMessage());
cause.printStackTrace();
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (evt instanceof IdleStateEvent event) {
if (event.state() == IdleState.READER_IDLE) {
try {
clientOfflineEvent(ctx.channel());
ctx.channel().close();
} catch (Exception e) {
} catch (Exception ignored) {
}
}
} else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
@@ -65,12 +74,16 @@ public class WebsocketTransferHandler extends SimpleChannelInboundHandler<TextWe
clientSide.setRole(ClientRole.BLACK_PLAYER);
ServerContains.CLIENT_SIDE_MAP.put(clientSide.getId(), clientSide);
SimplePrinter.serverLog("Has client connect to the server" + clientSide.getId());
SimplePrinter.serverLog("Has client connect to the server: " + clientSide.getId());
new Thread(() -> {
try {
Thread.sleep(2000L);
ChannelUtils.pushToClient(ch, ClientEventCode.CODE_CLIENT_CONNECT, String.valueOf(clientSide.getId()));
ChannelUtils.pushToClient(ch, ClientEventCode.CODE_CLIENT_NICKNAME_SET, null);
ChannelUtils.push(ch, Response.newBuilder()
.setClientConnect(ClientConnectResponse.newBuilder().setClientId(clientSide.getId()))
.build());
ChannelUtils.push(ch, Response.newBuilder()
.setNicknameSet(NicknameSetResponse.newBuilder().setInvalidLength(0))
.build());
} catch (InterruptedException ignored) {
}
}).start();
@@ -93,8 +106,8 @@ public class WebsocketTransferHandler extends SimpleChannelInboundHandler<TextWe
int clientId = getId(channel);
ClientSide client = ServerContains.CLIENT_SIDE_MAP.get(clientId);
if (client != null) {
SimplePrinter.serverLog("Has client exit to the server" + clientId + " | " + client.getNickname());
ServerEventListener.get(ServerEventCode.CODE_CLIENT_OFFLINE).call(client, null);
SimplePrinter.serverLog("Has client exit to the server: " + clientId + " | " + client.getNickname());
// TODO phase 02b: wire to ClientOfflineHandler.handle(client)
}
}
}
@@ -1,68 +0,0 @@
package com.miti99.caro.server.proxy;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.timeout.IdleStateHandler;
import com.miti99.caro.common.entity.ServerTransferData;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.handler.SecondProtobufCodec;
import com.miti99.caro.server.handler.ProtobufTransferHandler;
import com.miti99.caro.server.timer.RoomClearTask;
import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
public class ProtobufProxy implements Proxy{
@Override
public void start(int port) throws InterruptedException {
EventLoopGroup parentGroup = Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
EventLoopGroup childGroup = Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap()
.group(parentGroup, childGroup)
.channel(Epoll.isAvailable() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
.addLast(new ProtobufVarint32FrameDecoder())
.addLast(new ProtobufDecoder(ServerTransferData.ServerTransferDataProtoc.getDefaultInstance()))
.addLast(new ProtobufVarint32LengthFieldPrepender())
.addLast(new ProtobufEncoder())
.addLast(new SecondProtobufCodec())
.addLast(new ProtobufTransferHandler());
}
});
ChannelFuture f = bootstrap .bind().sync();
SimplePrinter.serverLog("The protobuf server was successfully started on port " + port);
ServerContains.THREAD_EXCUTER.execute(() -> {
Timer timer=new Timer();
timer.schedule(new RoomClearTask(), 0L, 3000L);
});
f.channel().closeFuture().sync();
} finally {
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
}
}
@@ -1,7 +0,0 @@
package com.miti99.caro.server.proxy;
public interface Proxy {
void start(int port) throws InterruptedException;
}
@@ -1,5 +1,14 @@
package com.miti99.caro.server.proxy;
import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.handler.WebsocketTransferHandler;
import com.miti99.caro.server.timer.RoomClearTask;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
@@ -15,19 +24,9 @@ import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.handler.ProtobufTransferHandler;
import com.miti99.caro.server.handler.WebsocketTransferHandler;
import com.miti99.caro.server.timer.RoomClearTask;
public class WebsocketProxy {
import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
public class WebsocketProxy implements Proxy{
@Override
public void start(int port) throws InterruptedException {
EventLoopGroup parentGroup = Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
EventLoopGroup childGroup = Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
@@ -38,7 +37,7 @@ public class WebsocketProxy implements Proxy{
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
.addLast(new HttpServerCodec())
@@ -49,14 +48,19 @@ public class WebsocketProxy implements Proxy{
}
});
ChannelFuture f = bootstrap .bind().sync();
ChannelFuture f = bootstrap.bind().sync();
SimplePrinter.serverLog("The websocket server was successfully started on port " + port);
ServerContains.THREAD_EXCUTER.execute(() -> {
Timer timer = new Timer();
timer.schedule(new RoomClearTask(), 0L, 3000L);
});
f.channel().closeFuture().sync();
} finally {
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
}
}
@@ -3,13 +3,10 @@ package com.miti99.caro.server.timer;
import java.util.Map;
import java.util.TimerTask;
import com.miti99.caro.common.entity.ClientSide;
import com.miti99.caro.common.entity.Room;
import com.miti99.caro.common.enums.RoomStatus;
import com.miti99.caro.common.enums.ServerEventCode;
import com.miti99.caro.common.print.SimplePrinter;
import com.miti99.caro.server.ServerContains;
import com.miti99.caro.server.event.ServerEventListener;
/**
* Periodically cleans up idle or expired rooms.
@@ -55,11 +52,7 @@ public class RoomClearTask extends TimerTask {
}
private void closeRoom(Room room) {
if (!room.getClientSideList().isEmpty()) {
ClientSide first = room.getClientSideList().get(0);
ServerEventListener.get(ServerEventCode.CODE_CLIENT_EXIT).call(first, null);
} else {
ServerContains.removeRoom(room.getId());
}
// TODO phase 02b: notify remaining clients via ClientExitHandler before removal.
ServerContains.removeRoom(room.getId());
}
}
+44
View File
@@ -0,0 +1,44 @@
syntax = "proto3";
package miti99.caro.protocol;
option java_package = "com.miti99.caro.protocol";
option java_multiple_files = true;
// Wrapper for all client -> server messages. The oneof case IS the event code.
message Request {
oneof payload {
HeartbeatRequest heartbeat = 1;
SetNicknameRequest set_nickname = 2;
SetClientInfoRequest set_client_info = 3;
CreateRoomRequest create_room = 4;
CreatePveRoomRequest create_pve_room = 5;
GetRoomsRequest get_rooms = 6;
JoinRoomRequest join_room = 7;
GameStartingRequest game_starting = 8;
GameReadyRequest game_ready = 9;
GameMoveRequest game_move = 10;
GameResetRequest game_reset = 11;
WatchGameRequest watch_game = 12;
WatchGameExitRequest watch_game_exit = 13;
ClientExitRequest client_exit = 14;
}
}
message HeartbeatRequest {}
message SetNicknameRequest { string nickname = 1; }
message SetClientInfoRequest { string version = 1; }
message CreateRoomRequest {}
message CreatePveRoomRequest { int32 difficulty = 1; }
message GetRoomsRequest {}
message JoinRoomRequest { int32 room_id = 1; }
message GameStartingRequest {}
message GameReadyRequest {}
message GameMoveRequest {
int32 row = 1;
int32 col = 2;
}
message GameResetRequest {}
message WatchGameRequest { int32 room_id = 1; }
message WatchGameExitRequest {}
message ClientExitRequest {}
+117
View File
@@ -0,0 +1,117 @@
syntax = "proto3";
package miti99.caro.protocol;
option java_package = "com.miti99.caro.protocol";
option java_multiple_files = true;
// Wrapper for all server -> client messages. The oneof case IS the event code.
message Response {
oneof payload {
ClientConnectResponse client_connect = 1;
NicknameSetResponse nickname_set = 2;
ShowOptionsResponse show_options = 3;
ShowRoomsResponse show_rooms = 4;
RoomCreateSuccessResponse room_create_success = 5;
RoomJoinSuccessResponse room_join_success = 6;
RoomJoinFailFullResponse room_join_fail_full = 7;
RoomJoinFailNotFoundResponse room_join_fail_not_found = 8;
RoomPlayFailNotFoundResponse room_play_fail_not_found = 9;
GameStartingResponse game_starting = 10;
GameReadyResponse game_ready = 11;
GameMoveSuccessResponse game_move_success = 12;
GameMoveInvalidResponse game_move_invalid = 13;
GameMoveOccupiedResponse game_move_occupied = 14;
GameMoveOutOfBoundsResponse game_move_out_of_bounds = 15;
GameMoveNotYourTurnResponse game_move_not_your_turn = 16;
GameOverResponse game_over = 17;
PveDifficultyNotSupportResponse pve_difficulty_not_support = 18;
WatchGameSuccessResponse watch_game_success = 19;
ClientExitResponse client_exit = 20;
}
}
message ClientConnectResponse { int32 client_id = 1; }
// invalid_length = 0 means "prompt only — ask the user for a nickname"
message NicknameSetResponse { int32 invalid_length = 1; }
message ShowOptionsResponse {}
message RoomSummary {
int32 room_id = 1;
string room_owner = 2;
int32 room_client_count = 3;
string room_type = 4;
}
message ShowRoomsResponse { repeated RoomSummary rooms = 1; }
message RoomCreateSuccessResponse {
int32 id = 1;
string room_owner = 2;
string room_type = 3;
}
message RoomJoinSuccessResponse {
int32 client_id = 1;
string client_nickname = 2;
int32 room_id = 3;
string room_owner = 4;
int32 room_client_count = 5;
}
message RoomJoinFailFullResponse {
int32 room_id = 1;
string room_owner = 2;
}
message RoomJoinFailNotFoundResponse { int32 room_id = 1; }
message RoomPlayFailNotFoundResponse {}
message GameStartingResponse {
int32 room_id = 1;
int32 black_player_id = 2;
string black_player_nickname = 3;
int32 white_player_id = 4;
string white_player_nickname = 5;
int32 board_size = 6;
}
message GameReadyResponse {
string client_nickname = 1;
string status = 2;
int32 client_id = 3;
}
message GameMoveSuccessResponse {
int32 row = 1;
int32 col = 2;
string piece = 3;
string player_nickname = 4;
int32 player_id = 5;
}
message GameMoveInvalidResponse {}
message GameMoveOccupiedResponse {}
message GameMoveOutOfBoundsResponse {}
message GameMoveNotYourTurnResponse {}
message GameOverResponse {
string result = 1;
string winner_nickname = 2;
}
message PveDifficultyNotSupportResponse {}
message WatchGameSuccessResponse {
string owner = 1;
string status = 2;
}
message ClientExitResponse {
int32 room_id = 1;
int32 exit_client_id = 2;
string exit_client_nickname = 3;
}
@@ -1,15 +0,0 @@
syntax = "proto3";
package com.miti99.caro.common.entity;
option java_package = "com.miti99.caro.common.entity";
option java_outer_classname = "ClientTransferData";
message ClientTransferDataProtoc{
string code = 1;
string data = 2;
string info = 3;
}
@@ -1,15 +0,0 @@
syntax = "proto3";
package com.miti99.caro.common.entity;
option java_package = "com.miti99.caro.common.entity";
option java_outer_classname = "ServerTransferData";
message ServerTransferDataProtoc{
string code = 1;
string data = 2;
string info = 3;
}
@@ -1,4 +0,0 @@
#!/usr/bin/env bash
# Regenerate protobuf Java classes from .proto files.
# Run from server/src/main/resources/proto/ directory.
protoc -I=. --java_out=../../../java/ ./*.proto