/* * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod * Copyright (C) 2021 cyoung06 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package kr.syeyoung.dungeonsguide.mod.stomp; import lombok.Getter; import net.minecraftforge.common.MinecraftForge; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class StompClient extends WebSocketClient { Logger logger = LogManager.getLogger("StompClient"); public StompClient(URI serverUri, final String token) throws InterruptedException { super(serverUri); addHeader("Authorization", token); logger.info("connecting websocket"); if (!connectBlocking()) { throw new FailedWebSocketConnection("Cant connect to ws"); } logger.info("connected, stomp handshake"); while(this.stompClientStatus == StompClientStatus.CONNECTING); logger.info("fully connected"); } @Getter private volatile StompClientStatus stompClientStatus = StompClientStatus.CONNECTING; @Getter private StompPayload errorPayload; private ScheduledFuture heartbeat = null; private static final ScheduledExecutorService ex = Executors.newScheduledThreadPool(1); @Override public void onOpen(ServerHandshake handshakedata) { send(new StompPayload().method(StompHeader.CONNECT) .header("accept-version","1.2") .header("heart-beat", "30000,30000") .header("host",uri.getHost()).getBuilt() ); } @Override public void onMessage(String message) { try { StompPayload payload = StompPayload.parse(message); switch (payload.method()){ case SEND: case SUBSCRIBE: case UNSUBSCRIBE: case BEGIN: case COMMIT: case ABORT: case ACK: case NACK: case DISCONNECT: case STOMP: break; case CONNECTED: stompClientStatus = StompClientStatus.CONNECTED; String serverHeartbeat = payload.headers().get("heart-beat"); if (serverHeartbeat != null) { int heartbeatMS = 30; this.heartbeat = ex.scheduleAtFixedRate(() -> send("\n"), heartbeatMS-1, heartbeatMS-1, TimeUnit.SECONDS); } break; case MESSAGE: String subscriptionName = payload.headers().get("subscription"); int subscriptionId = Integer.parseInt(subscriptionName); StompSubscription listener = stompSubscriptionMap.get(subscriptionId); listener.process(this, payload.payload()); break; case RECEIPT: String receiptId = payload.headers().get("receipt-id"); StompPayload payload1 = receiptMap.remove(Integer.parseInt(receiptId)); if (payload1.method() == StompHeader.DISCONNECT) { stompClientStatus = StompClientStatus.DISCONNECTED; close(); } break; case ERROR: errorPayload = payload; stompClientStatus = StompClientStatus.ERROR; this.close(); break; } } catch (Exception e) { e.printStackTrace(); } } @Override public void onClose(int code, String reason, boolean remote) { if (heartbeat != null) heartbeat.cancel(true); MinecraftForge.EVENT_BUS.post(new StompDiedEvent(code, reason, remote)); } @Override public void onError(Exception ex) { if(ex != null){ ex.printStackTrace(); } } private final Map stompSubscriptionMap = new HashMap<>(); private final Map receiptMap = new HashMap<>(); private int idIncrement = 0; private void makeSureStompIsConnected() { if (stompClientStatus != StompClientStatus.CONNECTED) throw new IllegalStateException("not connected"); } public void sendfake(StompPayload payload) { makeSureStompIsConnected(); payload.method(StompHeader.SEND); if (payload.headers().get("receipt") != null) receiptMap.put(Integer.parseInt(payload.headers().get("receipt")), payload); send(payload.getBuilt()); } public void subscribe(String destination, StompSubscription listener) { makeSureStompIsConnected(); int id = ++idIncrement; send(new StompPayload() .method(StompHeader.SUBSCRIBE) .header("id", String.valueOf(id)) .destination(destination) .header("ack", "auto") .getBuilt() ); stompSubscriptionMap.put(id, listener); } public void disconnect() { makeSureStompIsConnected(); stompClientStatus =StompClientStatus.DISCONNECTING; StompPayload stompPayload = new StompPayload().method(StompHeader.DISCONNECT).header("receipt", String.valueOf(++idIncrement)); send(stompPayload.getBuilt()); receiptMap.put(idIncrement, stompPayload); } public enum StompClientStatus { CONNECTING, CONNECTED, ERROR, DISCONNECTING, DISCONNECTED } }