Skip to content

Commit

Permalink
Merge pull request #64 from flytegg/pr/redis
Browse files Browse the repository at this point in the history
Pr/redis
  • Loading branch information
joshbker authored Apr 5, 2024
2 parents 59ab047 + d5295a4 commit e3c8852
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 4 deletions.
100 changes: 97 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Maven
<dependency>
<groupId>gg.flyte</groupId>
<artifactId>twilight</artifactId>
<version>1.1.9</version>
<version>1.1.10</version>
</dependency>
```

Expand All @@ -33,14 +33,14 @@ maven {
url "https://repo.flyte.gg/releases"
}
implementation "gg.flyte:twilight:1.1.9"
implementation "gg.flyte:twilight:1.1.10"
```

Gradle (Kotlin DSL)
```kotlin
maven("https://repo.flyte.gg/releases")

implementation("gg.flyte:twilight:1.1.9")
implementation("gg.flyte:twilight:1.1.10")
```

Certain features of Twilight require configuration, which can be done via the Twilight class. To setup a Twilight class instance, you can use the `twilight` function as shown below:
Expand Down Expand Up @@ -428,6 +428,100 @@ NameCacheService.uuidFromName("stxphen")

Currently the only way to configure your MongoDB "cache" for UUIDs and names, is to have an Environment variable called `NAME_CACHE_COLLECTION` with the value being what you want to call the collection. Don't want to use the Mongo cache? Disable `useMongoCache` in the settings.

# Redis
Twilight has a Redis system that lets you set/get/delete string key value pairs, additionally, you are able to publish messages and listen to incoming messages on any channel you'd like.

#### Environment variables
You can use the following Environment variables for your Redis Server:
```env
REDIS_HOST="your redis server host"
REDIS_PORT="your redis server port"
REDIS_TIMEOUT="your redis connection timeout"
REDIS_USING_PASSWORD="false"
REDIS_USERNAME:""
REDIS_PASSWORD:""
```
Alternativley, if your Redis server requires a Username + Password in order to access, you can use the following:
```env
REDIS_HOST="your redis server host"
REDIS_PORT="your redis server port"
REDIS_TIMEOUT="your redis connection timeout"
REDIS_USING_PASSWORD="true"
REDIS_USERNAME:"coolUsername"
REDIS_PASSWORD:"coolPassword"
```
#### Builder
When building your Twilight instance, you can specify your host and port like so:
```kotlin
val twilight = twilight(plugin) {
redis {
host = "your redis server host"
port = 6379 // Default Redis Port
timeout = 500 // 500 Milliseconds Timeout
isUsingPassword = false // False by default
}
}
```
Alternativley, if your Redis server requires a Username + Password in order to access, you can use the following:
```kotlin
val twilight = twilight(plugin) {
redis {
host = "your redis server host"
port = 6379 // Default Redis Port
timeout = 500 // 500 Milliseconds Timeout
isUsingPassword = true
username = "coolUsername"
password = "coolPassword"
}
}
```
#### String Key-Value Pairs
You can Set/Get/Delete String Key-Value pairs on your Redis server like so: (All of those functions are Async and return a CompleteableFuture)
```kotlin
Redis.set("cool-key", "super-secret-value")

val future = Redis.get("cool-key") // Returns a Completable Future

future.thenApplyAsync {
value -> println("The value is: $value") // Prints: "The value is: super-secret-value"
}.exceptionally {
e -> println("An exception occurred: ${e.message}") // Handle the Exception
}

Thread.sleep(1000)

Redis.delete("cool-key")
```
#### Publishing Messages
You can publish messages like so:

```kotlin
Redis.publish("channel", "message") // Async Publishing
```
#### Redis Listeners (PubSub)
##### Listen to incoming messages
You are able to listen to incoming message on specific channels, using the 'TwilightRedisListener' Class:
```kotlin
// Extend the 'TwilightRedisListener' class and override the 'onMessage' function.
class PlayerConnectionRedisListener(): TwilightRedisListener("player-connection") {
override fun onMessage(message: String) {
// do stuff
}
}
```
You can add add/register the listener like this: (which also returns the listener which lets you unregister it if you'd like)
```kotlin
val listener = Redis.addListener(PlayerConnectionRedisListener())
listener.unregister() // unregistering the listener.
```
Alternativley, instead of extending the listener class, you can add a listener using a block of code, which returns the 'RedisMessage' data class, which contains the channel, the message, and the listener:
```kotlin
val listener = Redis.addListener("cool-channel"){
println("The following message was received: '$message' on channel '$channel'")
this.listener.unregister() // unregistering the listener after we recieved the message.
}
```

### Files Extensions

#### File.hash()
Expand Down
4 changes: 3 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = "gg.flyte"
version = "1.1.9"
version = "1.1.10"

repositories {
mavenLocal()
Expand All @@ -27,6 +27,8 @@ dependencies {
api("io.github.cdimascio:dotenv-kotlin:6.4.1")
api("org.mongodb:mongodb-driver-kotlin-sync:4.11.0")
api("com.google.code.gson:gson:2.10.1")
api("redis.clients:jedis:5.1.2")


implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0-RC2")
implementation(kotlin("reflect"))
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/gg/flyte/twilight/Twilight.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import gg.flyte.twilight.environment.Environment
import gg.flyte.twilight.event.custom.chat.command.ChatClickCommand
import gg.flyte.twilight.event.customEventListeners
import gg.flyte.twilight.extension.applyForEach
import gg.flyte.twilight.data.Redis
import gg.flyte.twilight.server.ServerSoftware
import org.bukkit.plugin.java.JavaPlugin

Expand Down Expand Up @@ -36,10 +37,13 @@ class Twilight(javaPlugin: JavaPlugin) {
fun env(init: Environment.Settings.() -> Unit = {}) {
usingEnv = true
Environment.env(Environment.Settings().apply(init))

}

fun mongo(init: MongoDB.Settings.() -> Unit = {}) = MongoDB.mongo(MongoDB.Settings().apply(init))

fun redis(init: Redis.Settings.() -> Unit = {}) = Redis.redis(Redis.Settings().apply(init))

fun nameCache(init: NameCacheService.Settings.() -> Unit = {}) = NameCacheService.nameCache(NameCacheService.Settings().apply(init))

fun terminate() = customEventListeners.applyForEach { unregister() }
Expand Down
67 changes: 67 additions & 0 deletions src/main/kotlin/gg/flyte/twilight/data/Redis.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package gg.flyte.twilight.data

import gg.flyte.twilight.Twilight
import gg.flyte.twilight.environment.Environment
import redis.clients.jedis.DefaultJedisClientConfig
import redis.clients.jedis.Jedis
import redis.clients.jedis.JedisPubSub
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor
import java.util.concurrent.Executors

object Redis {
private lateinit var jedis: Jedis
private val executor: Executor = Executors.newCachedThreadPool()
fun redis(redis: Settings) {
if (redis.isUsingPassword){
val config = DefaultJedisClientConfig.builder().user(redis.username).password(redis.password).timeoutMillis(redis.timeout).build()
jedis = Jedis(redis.host, redis.port, config)
return
}
jedis = Jedis(redis.host, redis.port, redis.timeout)
}
private fun publishSync(channel: String, message: String) = jedis.publish(channel, message)
fun publish(channel: String, message: String): CompletableFuture<Long> = CompletableFuture.supplyAsync({ publishSync(channel, message) }, executor)
private fun setSync(key: String, value: String) = jedis.set(key, value)
fun set(key: String, value: String): CompletableFuture<String> = CompletableFuture.supplyAsync({ setSync(key, value) }, executor)
private fun getSync(key: String) = jedis.get(key)
fun get(key: String): CompletableFuture<String> = CompletableFuture.supplyAsync({ getSync(key) }, executor)
private fun deleteSync(key: String) = jedis.del(key)
fun delete(key: String): CompletableFuture<Long> = CompletableFuture.supplyAsync({ deleteSync(key) }, executor)

fun addListener(listener: TwilightRedisListener): TwilightRedisListener {
jedis.subscribe(listener, listener.channel)
return listener
}
fun addListener(channel: String, block: RedisMessage.() -> Unit): TwilightRedisListener {
val listener = RedisListener(channel, block)
jedis.subscribe(listener, channel)
return listener
}
class Settings {
var host: String = if (Twilight.usingEnv) Environment.get("REDIS_HOST") else "localhost"
var port: Int = if (Twilight.usingEnv) Environment.get("REDIS_PORT").toInt() else 6379
var timeout: Int = if (Twilight.usingEnv) Environment.get("REDIS_TIMEOUT").toInt() else 0
var isUsingPassword: Boolean = if (Twilight.usingEnv) Environment.get("REDIS_USING_PASSWORD").toBoolean() else false
val username: String = if (Twilight.usingEnv && isUsingPassword) Environment.get("REDIS_USERNAME") else ""
var password: String = if (Twilight.usingEnv && isUsingPassword) Environment.get("REDIS_PASSWORD") else ""
}
}

data class RedisMessage(val channel: String, val message: String, val listener: TwilightRedisListener)

abstract class TwilightRedisListener(val channel: String) : JedisPubSub() {
override fun onMessage(channel: String?, message: String?) {
channel ?: return
message?: return
if (channel == this.channel) onMessage(message)
}
abstract fun onMessage(message: String)
fun unregister() = unsubscribe()
}

class RedisListener(channel: String, val block: RedisMessage.() -> Unit): TwilightRedisListener(channel) {
override fun onMessage(message: String) {
block(RedisMessage(channel, message, this))
}
}
30 changes: 30 additions & 0 deletions src/test/kotlin/gg/flyte/twilight/RedisTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package gg.flyte.twilight

import gg.flyte.twilight.data.Redis

fun addingListenersTest(){

val listener = Redis.addListener("cool-channel"){
println("The following message was received: '$message' on channel '$channel'")
this.listener.unregister()
}


Redis.set("cool-key", "super-secret-value")

val future = Redis.get("cool-key") // Returns a Completable Future

future.thenApplyAsync {
value -> println("The value is: $value") // Prints: "The value is: super-secret-value"
}.exceptionally {
e -> println("An exception occurred: ${e.message}") // Handle the Exception
}

Thread.sleep(1000)

Redis.delete("cool-key")




}

0 comments on commit e3c8852

Please sign in to comment.