package org.openautonomousconnection.protocol.packets; import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; import lombok.Getter; import org.openautonomousconnection.protocol.versions.ProtocolVersion; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; /** * Abstract class representing a packet in the Open Autonomous Connection (OAC) protocol. * This class extends the base Packet class and includes additional functionality specific to OAC. */ public abstract class OACPacket extends Packet { /** * The protocol version associated with this packet. */ @Getter private final ProtocolVersion protocolVersion; private final int id; /** * The response code for the packet, defaulting to RESPONSE_NOT_REQUIRED. */ private INSResponseStatus responseCode = INSResponseStatus.RESPONSE_NOT_REQUIRED; /** * Constructor for OACPacket. * * @param id The unique identifier for the packet. * @param protocolVersion The protocol version associated with this packet. */ public OACPacket(int id, ProtocolVersion protocolVersion) { this.id = id; this.protocolVersion = protocolVersion; } @Override public int getPacketID() { return id; } /** * Gets the response code for the packet. * * @return The INSResponseCode associated with the packet. */ protected final INSResponseStatus getResponseCode() { return responseCode; } /** * Sets the response code for the packet. * * @param responseCode The INSResponseCode to set for the packet. */ protected final void setResponseCode(INSResponseStatus responseCode) { this.responseCode = responseCode; } /** * Writes the packet data to the output stream. * * @param outputStream The output stream to write the packet data to. * @throws IOException If an I/O error occurs. */ @Override public final void write(DataOutputStream outputStream) throws IOException { // Write the specific packet data onWrite(outputStream); // Write the response code if the protocol version is not classic if (protocolVersion != ProtocolVersion.PV_1_0_0_CLASSIC) outputStream.writeUTF(responseCode.name()); } @Override public final void read(DataInputStream inputStream, UUID clientID) throws IOException { // Read the specific packet data onRead(inputStream, clientID); // Read the response code if the protocol version is not classic if (protocolVersion != ProtocolVersion.PV_1_0_0_CLASSIC) responseCode = INSResponseStatus.valueOf(inputStream.readUTF()); else responseCode = INSResponseStatus.RESPONSE_NOT_REQUIRED; // Call the response code read handler onResponseCodeRead(inputStream, clientID); } /** * Abstract method to be implemented by subclasses for writing specific packet data. * * @param outputStream The output stream to write the packet data to. * @throws IOException If an I/O error occurs. */ public abstract void onWrite(DataOutputStream outputStream) throws IOException; /** * Abstract method to be implemented by subclasses for reading specific packet data. * * @param inputStream The input stream to read the packet data from. * @throws IOException If an I/O error occurs. */ public abstract void onRead(DataInputStream inputStream, UUID clientID) throws IOException; /** * Method called after the response code has been read from the input stream. * Subclasses can override this method to handle any additional logic based on the response code. * * @param inputStream The input stream from which the response code was read. */ protected void onResponseCodeRead(DataInputStream inputStream, UUID clientID) { } /** * Writes a string map in a deterministic way (no Java object serialization). * * @param out output stream * @param map map to write (may be null) * @throws IOException on I/O errors */ protected final void writeStringMap(DataOutputStream out, Map map) throws IOException { if (map == null || map.isEmpty()) { out.writeInt(0); return; } out.writeInt(map.size()); for (Map.Entry e : map.entrySet()) { // Null keys/values are normalized to empty strings to keep the wire format stable. out.writeUTF((e.getKey() != null) ? e.getKey() : ""); out.writeUTF((e.getValue() != null) ? e.getValue() : ""); } } /** * Reads a string map in a deterministic way (no Java object serialization). * * @param in input stream * @return headers map (never null) * @throws IOException on I/O errors / invalid sizes */ protected final Map readStringMap(DataInputStream in) throws IOException { int size = in.readInt(); if (size < 0) { throw new IOException("Negative map size"); } if (size == 0) { return Collections.emptyMap(); } Map map = new LinkedHashMap<>(Math.max(16, size * 2)); for (int i = 0; i < size; i++) { String key = in.readUTF(); String value = in.readUTF(); map.put(key, value); } return map; } }