diff --git a/src/main/kotlin/civilengineering/CivilEngineering.kt b/src/main/kotlin/civilengineering/CivilEngineering.kt index aa5ebec..b4cff88 100644 --- a/src/main/kotlin/civilengineering/CivilEngineering.kt +++ b/src/main/kotlin/civilengineering/CivilEngineering.kt @@ -7,7 +7,6 @@ import civilengineering.eventhandlers.ChatMessageHandler import civilengineering.eventhandlers.DeathEventHandler import civilengineering.eventhandlers.UserActionHandler import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.common.config.Configuration import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.event.* import org.apache.logging.log4j.Level @@ -15,7 +14,6 @@ import org.apache.logging.log4j.Logger import org.apache.logging.log4j.message.SimpleMessageFactory import org.apache.logging.log4j.simple.SimpleLogger import org.apache.logging.log4j.util.PropertiesUtil -import java.io.File import java.util.* const val MODID = "civilengineering" @@ -31,8 +29,6 @@ const val VERSION = "0.0.1" modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter" ) object CivilEngineering { - var config: Configuration = Configuration() - //create fake logger to get around Nullability var logger: Logger = SimpleLogger("", Level.OFF, @@ -50,10 +46,7 @@ object CivilEngineering { logger = event.modLog logger.info("loading logger") - CivilEngineering.logger.info("Reading bridge blueprints...") - val directory = event.modConfigurationDirectory - config = Configuration(File(directory.path, "CivilEngineering.cfg")) - Config.readConfig() + CivilEngineeringConfig(event.modConfigurationDirectory) } @Mod.EventHandler @@ -63,9 +56,6 @@ object CivilEngineering { @Mod.EventHandler fun postInit(event: FMLPostInitializationEvent) { - if (config.hasChanged()) { - config.save() - } // MinecraftForge.EVENT_BUS.register(ServerChatHelper::class.java) } diff --git a/src/main/kotlin/civilengineering/CivilEngineeringConfig.kt b/src/main/kotlin/civilengineering/CivilEngineeringConfig.kt new file mode 100644 index 0000000..4d4f4d9 --- /dev/null +++ b/src/main/kotlin/civilengineering/CivilEngineeringConfig.kt @@ -0,0 +1,80 @@ +package civilengineering + +import net.minecraftforge.common.config.Configuration +import java.io.File + +var cfg: CivilEngineeringConfig? = null + +class CivilEngineeringConfig(file: File) { + private val CATEGORY_RELAY_OPTIONS = "relay" + private val CATEGORY_CONNECTION = "connection" + + val relay: RelayOptions + val connect: ConnectOptions + + data class RelayOptions( + val deathEvents: Boolean, + val advancements: Boolean, + val joinLeave: Boolean + ) + + data class ConnectOptions( + val url: String, + val authToken: String, + val gateway: String + ) + + init { + CivilEngineering.logger.info("Reading bridge blueprints...") + val config = Configuration(file.resolve("CivilEngineering.cfg")) + + config.addCustomCategoryComment(CATEGORY_RELAY_OPTIONS, "Relay options") + config.addCustomCategoryComment(CATEGORY_CONNECTION, "Connection settings") + + relay = RelayOptions( + deathEvents = config.getBoolean( + "deathEvents", + CATEGORY_RELAY_OPTIONS, + false, + "Relay player death messages" + ), + advancements = config.getBoolean( + "advancements", + CATEGORY_RELAY_OPTIONS, + false, + "Relay player advancements [NOT IMPLEMENTED]" + ), + joinLeave = config.getBoolean( + "joinLeave", + CATEGORY_RELAY_OPTIONS, + false, + "Relay when a player joins or leaves the game [NOT IMPLEMENTED]" + ) + ) + + connect = ConnectOptions( + url = config.getString( + "connectURL", + CATEGORY_CONNECTION, + "http://example.com:1234", + "The URL or IP address of the bridge server" + ), + authToken = config.getString( + "authToken", + CATEGORY_CONNECTION, + "", + "Auth token used to connect to the bridge server" + ), + gateway = config.getString( + "gateway", + CATEGORY_CONNECTION, + "minecraft", + "MatterBridge gateway" + ) + ) + + if (config.hasChanged()) config.save() + + cfg = this + } +} \ No newline at end of file diff --git a/src/main/kotlin/civilengineering/Config.kt b/src/main/kotlin/civilengineering/Config.kt deleted file mode 100644 index 4a29f1f..0000000 --- a/src/main/kotlin/civilengineering/Config.kt +++ /dev/null @@ -1,76 +0,0 @@ -package civilengineering - -import net.minecraftforge.common.config.Configuration -import org.apache.logging.log4j.Level - -object Config { - private val CATEGORY_RELAY_OPTIONS = "relay_options" - private val CATEGORY_CONNECTION = "connection" - - var relayDeathEvents = false - var relayAdvancements = false //unused for now - var relayJoinLeave = false - - var connectURL = "http://localhost" - var authToken = "" - var gateway = "" - - fun readConfig() { - - val config = CivilEngineering.config - try { - config.load() - initConfig(config) - } catch (e: Exception) { - CivilEngineering.logger!!.log(Level.ERROR, "Could not read config file!", e) - } finally { - if (config.hasChanged()) { - config.save() - } - } - } - - private fun initConfig(cfg: Configuration) { - cfg.addCustomCategoryComment(CATEGORY_RELAY_OPTIONS, "Relay options") - cfg.addCustomCategoryComment(CATEGORY_CONNECTION, "Connection settings") - relayDeathEvents = cfg.getBoolean( - "relayDeathEvents", - CATEGORY_RELAY_OPTIONS, - false, - "Relay player death messages" - ) - relayAdvancements = cfg.getBoolean( - "relayAdvancements", - CATEGORY_RELAY_OPTIONS, - false, - "Relay player advancements [NOT IMPLEMENTED]" - ) - relayJoinLeave = cfg.getBoolean( - "relayJoinLeave", - CATEGORY_RELAY_OPTIONS, - false, - "Relay when a player joins or leaves the game [NOT IMPLEMENTED]" - ) - - connectURL = cfg.getString( - "connectURL", - CATEGORY_CONNECTION, - "http://example.com:1234", - "The URL or IP address of the bridge server, ex. http://example.com:1234" - ) - authToken = cfg.getString( - "auth_token", - CATEGORY_CONNECTION, - "", - "Auth token used to connect to the bridge server" - ) - gateway = cfg.getString( - "gateway", - CATEGORY_CONNECTION, - "", - "MatterBridge gateway" - ) - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/civilengineering/bridge/ApiMessage.kt b/src/main/kotlin/civilengineering/bridge/ApiMessage.kt index a67129f..c2f5cd8 100644 --- a/src/main/kotlin/civilengineering/bridge/ApiMessage.kt +++ b/src/main/kotlin/civilengineering/bridge/ApiMessage.kt @@ -1,12 +1,12 @@ package civilengineering.bridge -import civilengineering.Config +import civilengineering.cfg import com.google.gson.Gson data class ApiMessage( val username: String = "", val text: String = "", - val gateway: String = Config.gateway, + val gateway: String = cfg!!.connect.gateway, val channel: String = "", val userid: String = "", val avatar: String = "", @@ -17,10 +17,6 @@ data class ApiMessage( val id: String = "" // val Extra: Any? = null ) { - fun encode(): String { - return gson.toJson(this) - } - companion object { val gson = Gson() @@ -28,4 +24,8 @@ data class ApiMessage( return gson.fromJson(json, ApiMessage::class.java) } } + + fun encode(): String { + return gson.toJson(this) + } } \ No newline at end of file diff --git a/src/main/kotlin/civilengineering/bridge/CancellableConnectionFollowThread.kt b/src/main/kotlin/civilengineering/bridge/CancellableConnectionFollowThread.kt deleted file mode 100644 index a18c7a8..0000000 --- a/src/main/kotlin/civilengineering/bridge/CancellableConnectionFollowThread.kt +++ /dev/null @@ -1,52 +0,0 @@ -package civilengineering.bridge - -import java.io.InputStreamReader -import java.net.HttpURLConnection - -/** - * Created by nikky on 20/01/18. - * @author Nikky - * @version 1.0 - */ - -class CancellableConnectionFollowThread(httpConnClosure: () -> HttpURLConnection, private val mhandler: (String) -> Unit) : Thread() { - private val cancelGuard = Object() - private var waitingOnNetwork = true - var cancelled = false - private set - private val httpConn = httpConnClosure() - - override fun run() { - try { - httpConn.allowUserInteraction = false - httpConn.instanceFollowRedirects = true - httpConn.requestMethod = "GET" - - InputStreamReader(httpConn.inputStream).useLines { - it.forEach { - synchronized(cancelGuard) { - if (cancelled) return - waitingOnNetwork = false - } - mhandler(it) - synchronized(cancelGuard) { - if (cancelled) return - waitingOnNetwork = true - } - } - } - } catch (e: Exception) { - } finally { - httpConn.disconnect() - } - } - - fun abort() { - synchronized(cancelGuard) { - httpConn.disconnect() - cancelled = true - if (waitingOnNetwork) stop() - } - join() - } -} \ No newline at end of file diff --git a/src/main/kotlin/civilengineering/bridge/HttpStreamConnection.kt b/src/main/kotlin/civilengineering/bridge/HttpStreamConnection.kt new file mode 100644 index 0000000..35ed1f7 --- /dev/null +++ b/src/main/kotlin/civilengineering/bridge/HttpStreamConnection.kt @@ -0,0 +1,61 @@ +package civilengineering.bridge; + +import civilengineering.CivilEngineering +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.HttpClients +import java.io.InputStream +import java.net.SocketException + +val BUFFER_SIZE = 1000 + +class HttpStreamConnection(private val getClosure: () -> HttpGet, private val mhandler: (String) -> Unit) : Thread() { + private val client = HttpClients.createDefault() + private var stream: InputStream? = null + + val get = getClosure() + var cancelled: Boolean = false + private set + + + override fun run() { + val response = client.execute(get) + val content = response.entity.content.buffered() + stream = content + //val reader = content.bufferedReader() + var buffer = "" + val buf = ByteArray(BUFFER_SIZE) + try { + while (!get.isAborted) { + val chars = content.read(buf) + CivilEngineering.logger.debug("incoming: $chars ") + if (chars > 0) { + buffer += String(buf.dropLast(buf.count() - chars).toByteArray()) + + CivilEngineering.logger.info(buffer) + + while (buffer.contains("\n")) { + val line = buffer.substringBefore("\n") + buffer = buffer.substringAfter("\n") + mhandler(line) + } + } else if (chars < 0) { + break + } + } + } catch (e: SocketException) { + + } + CivilEngineering.logger.info("closing stream") + content.close() + CivilEngineering.logger.info("thread finished") + return + } + + fun close() { + cancelled = true + get.abort() + join() + println("killed thread") + + } +} \ No newline at end of file diff --git a/src/main/kotlin/civilengineering/bridge/MessageHandler.kt b/src/main/kotlin/civilengineering/bridge/MessageHandler.kt index 5b2c848..91342b2 100644 --- a/src/main/kotlin/civilengineering/bridge/MessageHandler.kt +++ b/src/main/kotlin/civilengineering/bridge/MessageHandler.kt @@ -1,24 +1,31 @@ package civilengineering.bridge import civilengineering.CivilEngineering -import civilengineering.Config -import java.io.DataOutputStream +import civilengineering.cfg +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.methods.HttpRequestBase +import org.apache.http.entity.ContentType +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.HttpClients import java.io.IOException -import java.net.HttpURLConnection -import java.net.URL import java.util.concurrent.ConcurrentLinkedQueue + object MessageHandler { - private fun createThread(): CancellableConnectionFollowThread { + fun HttpRequestBase.authorize() { + if (cfg!!.connect.authToken.isNotEmpty() && getHeaders("Authorization").isEmpty()) + setHeader("Authorization", "Bearer " + cfg!!.connect.authToken) + } + + private fun createThread(): HttpStreamConnection { CivilEngineering.logger.info("building bridge") - return CancellableConnectionFollowThread( + return HttpStreamConnection( { - CivilEngineering.logger.info("Connecting to bridge server @ " + Config.connectURL) - val httpConn = URL(Config.connectURL + "/api/stream").openConnection() as HttpURLConnection - if (Config.authToken.isNotBlank()) - httpConn.setRequestProperty("Authorization", "Bearer ${Config.authToken}") - httpConn + HttpGet(cfg!!.connect.url + "/api/stream").apply { + authorize() + } }, { rcvQueue.add( @@ -29,9 +36,7 @@ object MessageHandler { ) } - private var cancellableThread: CancellableConnectionFollowThread = createThread() - - private var xmitQueue = ConcurrentLinkedQueue() + private var streamConnection: HttpStreamConnection = createThread() var rcvQueue = ConcurrentLinkedQueue() @@ -43,16 +48,17 @@ object MessageHandler { fun stop() { CivilEngineering.logger.info("bridge closing") // MessageHandler.transmit(ApiMessage(text="bridge closing", username="Server")) - cancellableThread.abort() + streamConnection.close() + CivilEngineering.logger.info("bridge closed") } fun start(): Boolean { - if (cancellableThread.cancelled) { - cancellableThread = createThread() + if (streamConnection.cancelled) { + streamConnection = createThread() } - if (!cancellableThread.isAlive) { - cancellableThread.start() + if (!streamConnection.isAlive) { + streamConnection.start() // MessageHandler.transmit(ApiMessage(text="bridge connected", username="Server")) return true } @@ -62,31 +68,16 @@ object MessageHandler { @Throws(IOException::class) private fun transmitMessage(message: ApiMessage) { //open a connection - val url = URL(Config.connectURL + "/api/message") - val urlConnection = url.openConnection() - val connection = urlConnection as HttpURLConnection + val client = HttpClients.createDefault() + val post = HttpPost(cfg!!.connect.url + "/api/message") - //configure the connection - connection.allowUserInteraction = false - connection.instanceFollowRedirects = true - connection.setRequestProperty("Content-Type", "application/json") - connection.requestMethod = "POST" - if (Config.authToken.isNotEmpty()) { - connection.setRequestProperty("Authorization", "Bearer " + Config.authToken) - } + post.entity = StringEntity(message.encode(), ContentType.APPLICATION_JSON) + post.authorize() - //encode the ApiMessage for sending - val json = message.encode() - - //send the message - connection.doOutput = true - val post = DataOutputStream(connection.outputStream) - post.writeBytes(json) - post.flush() - post.close() - - if (connection.responseCode != 200) { - CivilEngineering.logger.error("Server returned " + connection.responseCode) + val response = client.execute(post) + val code = response.statusLine.statusCode + if (code != 200) { + CivilEngineering.logger.error("Server returned $code for $post") } } } diff --git a/src/main/kotlin/civilengineering/eventhandlers/DeathEventHandler.kt b/src/main/kotlin/civilengineering/eventhandlers/DeathEventHandler.kt index 387a01b..9e52d03 100644 --- a/src/main/kotlin/civilengineering/eventhandlers/DeathEventHandler.kt +++ b/src/main/kotlin/civilengineering/eventhandlers/DeathEventHandler.kt @@ -1,8 +1,8 @@ package civilengineering.eventhandlers -import civilengineering.Config import civilengineering.bridge.ApiMessage import civilengineering.bridge.MessageHandler +import civilengineering.cfg import net.minecraft.entity.player.EntityPlayer import net.minecraftforge.event.entity.living.LivingDeathEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -10,7 +10,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class DeathEventHandler { @SubscribeEvent fun handleLivingDeathEvent(event: LivingDeathEvent) { - if (Config.relayDeathEvents) { + if (cfg!!.relay.deathEvents) { val entity = event.entityLiving if (entity is EntityPlayer) { val message = entity.getCombatTracker().deathMessage.unformattedText