333 lines
12 KiB
Kotlin
333 lines
12 KiB
Kotlin
package org.hso.texturesyncclient.controller.net
|
|
|
|
import com.google.gson.*
|
|
import javafx.scene.image.Image
|
|
import org.hso.texturesyncclient.model.Sha256
|
|
import org.hso.texturesyncclient.model.Texture
|
|
import java.io.*
|
|
import java.net.InetAddress
|
|
import java.net.InetSocketAddress
|
|
import java.net.Socket
|
|
import java.util.*
|
|
|
|
@Suppress("MemberVisibilityCanBePrivate")
|
|
class Connection(val address: InetAddress, val port: Int = 10796) : Closeable {
|
|
|
|
var socket: Socket? = null
|
|
var output: DataOutputStream? = null
|
|
var input: DataInputStream? = null
|
|
|
|
@Throws(IOException::class)
|
|
@Synchronized
|
|
private fun <R> withStreams(retry: Boolean = true, inner: (DataInputStream, DataOutputStream) -> R): R {
|
|
val i: DataInputStream
|
|
val o: DataOutputStream
|
|
|
|
if (socket == null || !socket!!.isConnected) {
|
|
val sock = Socket()
|
|
sock.soTimeout = 10_000 /*ms*/
|
|
sock.keepAlive = true
|
|
sock.connect(InetSocketAddress(address, port), 1_000 /*ms*/)
|
|
|
|
i = DataInputStream(BufferedInputStream(sock.getInputStream()))
|
|
o = DataOutputStream(BufferedOutputStream(sock.getOutputStream()))
|
|
|
|
input = i
|
|
output = o
|
|
socket = sock
|
|
} else {
|
|
i = input!!
|
|
o = output!!
|
|
}
|
|
|
|
return try {
|
|
inner(i, o)
|
|
} catch (e: IOException) {
|
|
if (retry) {
|
|
println("Got IOException; Retry ${e.message}")
|
|
close()
|
|
withStreams(false, inner)
|
|
} else {
|
|
throw e
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class)
|
|
fun ping() {
|
|
return withStreams { i, o ->
|
|
|
|
val obj = JsonObject()
|
|
obj.add("ping", JsonObject())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> return@withStreams
|
|
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
};
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class)
|
|
fun query(query: Array<String>): Array<Texture> {
|
|
return withStreams { i, o ->
|
|
|
|
val obj = JsonObject()
|
|
obj.add("query", {
|
|
val inner = JsonObject()
|
|
inner.add("query", {
|
|
val array = JsonArray()
|
|
for (queryString in query) {
|
|
array.add(queryString)
|
|
}
|
|
array
|
|
}())
|
|
inner
|
|
}())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> {
|
|
try {
|
|
return@withStreams Gson().fromJson<Array<InternalTexture>>(
|
|
pkg.content,
|
|
Array<InternalTexture>::class.java
|
|
)
|
|
.map { tex ->
|
|
tex.toTexture()
|
|
}.toTypedArray()
|
|
} catch (e: JsonSyntaxException) {
|
|
throw ConnectionInvalidJsonException()
|
|
}
|
|
}
|
|
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class)
|
|
fun getTextureById(id: UUID): Texture? {
|
|
return withStreams { i, o ->
|
|
|
|
|
|
val obj = JsonObject()
|
|
obj.add("get_texture", {
|
|
val inner = JsonObject()
|
|
inner.addProperty("id", id.toString())
|
|
inner
|
|
}())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> {
|
|
return@withStreams if (pkg.content.isJsonNull) {
|
|
null
|
|
} else {
|
|
try {
|
|
Gson()
|
|
.fromJson<InternalTexture>(pkg.content, InternalTexture::class.java)
|
|
.toTexture()
|
|
} catch (e: JsonSyntaxException) {
|
|
throw ConnectionInvalidJsonException()
|
|
}
|
|
}
|
|
}
|
|
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class)
|
|
fun getTextureByName(name: String): Texture? {
|
|
return withStreams { i, o ->
|
|
|
|
val obj = JsonObject()
|
|
obj.add("get_texture", {
|
|
val inner = JsonObject()
|
|
inner.addProperty("name", name)
|
|
inner
|
|
}())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> {
|
|
return@withStreams if (pkg.content.isJsonNull) {
|
|
null
|
|
} else {
|
|
try {
|
|
Gson()
|
|
.fromJson<InternalTexture>(pkg.content, InternalTexture::class.java)
|
|
.toTexture()
|
|
} catch (e: JsonSyntaxException) {
|
|
throw ConnectionInvalidJsonException()
|
|
}
|
|
}
|
|
}
|
|
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class)
|
|
fun getTextureFile(hash: Sha256): ByteArray {
|
|
return withStreams { i, o ->
|
|
|
|
val obj = JsonObject()
|
|
obj.add("get_texture_file", {
|
|
val inner = JsonObject()
|
|
inner.addProperty("texture_hash", hash.toString())
|
|
inner
|
|
}())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> throw ConnectionUnexpectedPacketException()
|
|
is BinaryPackage -> return@withStreams pkg.content
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class)
|
|
fun getTexturePreview(hash: Sha256): Image {
|
|
return withStreams { i, o ->
|
|
val obj = JsonObject()
|
|
obj.add("get_texture_preview", {
|
|
val inner = JsonObject()
|
|
inner.addProperty("texture_hash", hash.toString())
|
|
inner.addProperty("desired_format", "jpeg")
|
|
inner
|
|
}())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> throw ConnectionUnexpectedPacketException()
|
|
is BinaryPackage -> {
|
|
return@withStreams Image(ByteArrayInputStream(pkg.content))
|
|
}
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
|
private fun replaceTexture(old: Texture?, new: Texture?, image: ByteArray?) {
|
|
return withStreams { i, o ->
|
|
|
|
|
|
val obj = JsonObject()
|
|
obj.add("replace_texture", {
|
|
val inner = JsonObject()
|
|
if (old != null) {
|
|
inner.add("old", Gson().toJsonTree(InternalTexture(old), InternalTexture::class.java))
|
|
} else {
|
|
inner.add("old", null)
|
|
}
|
|
if (new != null) {
|
|
inner.add("new", Gson().toJsonTree(InternalTexture(new), InternalTexture::class.java))
|
|
} else {
|
|
inner.add("new", null)
|
|
}
|
|
inner
|
|
}())
|
|
|
|
JsonPackage(obj).write(o)
|
|
|
|
when (val pkg = Package.read(i)) {
|
|
is JsonPackage -> {
|
|
when {
|
|
pkg.content == JsonPrimitive(true) -> // everthing is fine!
|
|
return@withStreams
|
|
image != null -> {
|
|
// should be { "get_texture_file": { texture_hash : <Hash> }}
|
|
// we don't check, since there is no good way to handle it.
|
|
|
|
BinaryPackage(image).write(o)
|
|
when (val ipkg = Package.read(i)) {
|
|
is JsonPackage -> {
|
|
if (ipkg.content != JsonPrimitive(true)) {
|
|
// Protokoll Assertion failed
|
|
throw ConnectionUnexpectedPacketException()
|
|
}
|
|
}
|
|
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
|
is ErrorPackage -> throw ConnectionErrorException(ipkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
}
|
|
else -> {
|
|
ErrorPackage(404, "Texture not found!").write(o)
|
|
close() // gets re-opened on next request.
|
|
throw IllegalArgumentException("Image Argument was needed.")
|
|
}
|
|
}
|
|
}
|
|
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
|
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
|
else -> throw RuntimeException("Unreachable")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
|
fun uploadTexture(texture: Texture, image: ByteArray) {
|
|
if (texture.textureHash != Sha256(image)) {
|
|
throw IllegalArgumentException("Sha256 of Image does not Match with Texture.")
|
|
}
|
|
|
|
replaceTexture(null, texture, image)
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
|
fun updateTexture(old: Texture, new: Texture, image: ByteArray) {
|
|
if (new.textureHash != Sha256(image)) {
|
|
throw IllegalArgumentException("Sha256 of Image does not Match with Texture.")
|
|
}
|
|
|
|
replaceTexture(old, new, image)
|
|
}
|
|
|
|
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
|
fun deleteTexture(texture: Texture) {
|
|
replaceTexture(texture, null, null)
|
|
}
|
|
|
|
@Throws(IOException::class)
|
|
override fun close() {
|
|
try {
|
|
if (output != null) {
|
|
output!!.close()
|
|
output = null
|
|
}
|
|
if (input != null) {
|
|
input!!.close()
|
|
input = null
|
|
}
|
|
if (socket != null) {
|
|
socket!!.close()
|
|
socket = null
|
|
}
|
|
} catch (e: IOException) {
|
|
// Ignore
|
|
}
|
|
}
|
|
|
|
}
|