Merge branch 'master' into con-rewrite
This commit is contained in:
@@ -14,6 +14,11 @@
|
|||||||
/Server/build/kotlin/
|
/Server/build/kotlin/
|
||||||
/Server/build/generated/
|
/Server/build/generated/
|
||||||
/Server/data/eco/bot_offers.json
|
/Server/data/eco/bot_offers.json
|
||||||
|
/File-Server/build/
|
||||||
|
/File-Server/build/classes/
|
||||||
|
/File-Server/build/tmp/
|
||||||
|
/File-Server/build/kotlin/
|
||||||
|
/File-Server/build/generated/
|
||||||
**/.idea/workspace.xml
|
**/.idea/workspace.xml
|
||||||
**/.idea/tasks.xml
|
**/.idea/tasks.xml
|
||||||
Server/**/*.class
|
Server/**/*.class
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
archivesBaseName = 'fileserver'
|
||||||
|
|
||||||
|
mainClassName = 'js5server.JS5Server'
|
||||||
|
|
||||||
|
group 'org.rs09'
|
||||||
|
version '1.0.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
|
||||||
|
/* Networking */
|
||||||
|
implementation "io.ktor:ktor-server-core:1.5.0"
|
||||||
|
implementation "io.ktor:ktor-network:1.5.0"
|
||||||
|
|
||||||
|
// Cache ops
|
||||||
|
implementation 'com.displee:rs-cache-library:6.8'
|
||||||
|
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes 'Main-Class': 'js5server.JS5Server'
|
||||||
|
}
|
||||||
|
from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
revision=530
|
||||||
|
subrevision=1
|
||||||
|
port=43593
|
||||||
|
cachePath=
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package js5server
|
||||||
|
|
||||||
|
import com.displee.cache.CacheLibrary
|
||||||
|
|
||||||
|
interface DataProvider {
|
||||||
|
fun data(index: Int, archive: Int): ByteArray?
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
operator fun invoke(cache: CacheLibrary) = object : DataProvider {
|
||||||
|
override fun data(index: Int, archive: Int) =
|
||||||
|
if (index == 255) {
|
||||||
|
cache.index255?.readArchiveSector(archive)?.data
|
||||||
|
} else {
|
||||||
|
cache.index(index).readArchiveSector(archive)?.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package js5server
|
||||||
|
|
||||||
|
import com.displee.cache.CacheLibrary
|
||||||
|
import io.ktor.utils.io.*
|
||||||
|
import js5server.ext.readUMedium
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class FileServer(
|
||||||
|
private val provider: DataProvider,
|
||||||
|
private val versionTable: ByteArray
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fulfills a request by sending the requested files data to the requester
|
||||||
|
*/
|
||||||
|
suspend fun fulfill(read: ByteReadChannel, write: ByteWriteChannel, prefetch: Boolean) {
|
||||||
|
val value = read.readUMedium()
|
||||||
|
val index = value shr 16
|
||||||
|
val archive = value and 0xffff
|
||||||
|
val data = data(index, archive) ?: return println("Unable to fulfill request $index $archive $prefetch.")
|
||||||
|
if (index == 255 && archive == 255) {
|
||||||
|
serve255(write, data)
|
||||||
|
} else {
|
||||||
|
serve(write, index, archive, data, prefetch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return data for an [index]'s [archive] file or [versionTable] when index and archive are both 255
|
||||||
|
*/
|
||||||
|
fun data(index: Int, archive: Int): ByteArray? {
|
||||||
|
if (index == 255 && archive == 255) {
|
||||||
|
return versionTable
|
||||||
|
}
|
||||||
|
return provider.data(index, archive)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes [source] [offset] [size] to [write] and starting at [headerSize] inserting a [SEPARATOR] every [split] bytes
|
||||||
|
*/
|
||||||
|
private suspend fun serve255(write: ByteWriteChannel, source: ByteArray) {
|
||||||
|
write.writeByte(255)
|
||||||
|
write.writeShort(255)
|
||||||
|
write.writeByte(0)
|
||||||
|
write.writeInt(source.size)
|
||||||
|
write.writeFully(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes response header followed by the contents of [data] to [write]
|
||||||
|
*/
|
||||||
|
suspend fun serve(write: ByteWriteChannel, index: Int, archive: Int, data: ByteArray, prefetch: Boolean) {
|
||||||
|
val compression = data[0].toInt()
|
||||||
|
val size = getInt(data[1], data[2], data[3], data[4]) + if (compression != 0) 8 else 4
|
||||||
|
write.writeByte(index)
|
||||||
|
write.writeShort(archive)
|
||||||
|
write.writeByte(if (prefetch) compression or 0x80 else compression)
|
||||||
|
serve(write, HEADER, data, OFFSET, size, SPLIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes [source] [offset] [size] to [write] and starting at [headerSize] inserting a [SEPARATOR] every [split] bytes
|
||||||
|
*/
|
||||||
|
suspend fun serve(write: ByteWriteChannel, headerSize: Int, source: ByteArray, offset: Int, size: Int, split: Int) {
|
||||||
|
var length = min(size, split - headerSize)
|
||||||
|
write.writeFully(source, offset, length)
|
||||||
|
var written = length
|
||||||
|
while (written < size) {
|
||||||
|
write.writeByte(SEPARATOR)
|
||||||
|
|
||||||
|
length = if (size - written < split) size - written else split - 1
|
||||||
|
write.writeFully(source, written + offset, length)
|
||||||
|
written += length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun getInt(b1: Byte, b2: Byte, b3: Byte, b4: Byte) = b1.toInt() shl 24 or (b2.toInt() and 0xff shl 16) or (b3.toInt() and 0xff shl 8) or (b4.toInt() and 0xff)
|
||||||
|
|
||||||
|
private const val SEPARATOR = 255
|
||||||
|
private const val HEADER = 4
|
||||||
|
private const val SPLIT = 512
|
||||||
|
private const val OFFSET = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of archive with [name] in [index]
|
||||||
|
*/
|
||||||
|
fun file(cacheLibrary: CacheLibrary, index: Int, name: String): Int {
|
||||||
|
val idx = cacheLibrary.index(index)
|
||||||
|
val archive = idx.archiveId(name)
|
||||||
|
if (archive == -1) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (idx.readArchiveSector(archive)?.size ?: 2) - 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of all archives in [index]
|
||||||
|
*/
|
||||||
|
fun archive(cache: CacheLibrary, index: Int): Int {
|
||||||
|
var total = 0
|
||||||
|
val idx = cache.index(index)
|
||||||
|
idx.archiveIds().forEach { archive ->
|
||||||
|
total += idx.readArchiveSector(archive)?.size ?: 0
|
||||||
|
}
|
||||||
|
total += cache.index255?.readArchiveSector(index)?.size ?: 0
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package js5server
|
||||||
|
|
||||||
|
import io.ktor.network.selector.*
|
||||||
|
import io.ktor.network.sockets.*
|
||||||
|
import io.ktor.utils.io.*
|
||||||
|
import js5server.ext.readMedium
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
class JS5Net(
|
||||||
|
private val server: FileServer,
|
||||||
|
private val revision: Int,
|
||||||
|
private val subRevision: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val exceptionHandler = CoroutineExceptionHandler { context, throwable ->
|
||||||
|
System.err.println("$context | $throwable")
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var dispatcher: ExecutorCoroutineDispatcher
|
||||||
|
private var running = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the server and begin creating a new coroutine for every new connection accepted
|
||||||
|
* @param threads a fixed number or 0 to dynamically allocate based on need
|
||||||
|
*/
|
||||||
|
fun start(port: Int, threads: Int) = runBlocking {
|
||||||
|
val executor = if (threads == 0) Executors.newCachedThreadPool() else Executors.newFixedThreadPool(threads)
|
||||||
|
dispatcher = executor.asCoroutineDispatcher()
|
||||||
|
val selector = ActorSelectorManager(dispatcher)
|
||||||
|
val supervisor = SupervisorJob()
|
||||||
|
val scope = CoroutineScope(coroutineContext + supervisor + exceptionHandler)
|
||||||
|
with(scope) {
|
||||||
|
val server = aSocket(selector).tcp().bind(port = port)
|
||||||
|
running = true
|
||||||
|
while (running) {
|
||||||
|
val socket = server.accept()
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
connect(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun connect(socket: Socket) {
|
||||||
|
val read = socket.openReadChannel()
|
||||||
|
val write = socket.openWriteChannel(autoFlush = true)
|
||||||
|
synchronise(read, write)
|
||||||
|
if (acknowledge(read, write)) {
|
||||||
|
readRequests(read, write)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the client is up-to-date and in the correct state send it the [prefetchKeys] list so it knows what indices are available to request
|
||||||
|
*/
|
||||||
|
private suspend fun synchronise(read: ByteReadChannel, write: ByteWriteChannel) {
|
||||||
|
val opcode = read.readByte().toInt()
|
||||||
|
println("Received $opcode")
|
||||||
|
if (opcode != HANDSHAKE_REQUEST) {
|
||||||
|
write.writeByte(REJECT_SESSION)
|
||||||
|
write.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val revision = read.readInt()
|
||||||
|
val version = read.readInt()
|
||||||
|
println("Received ${this.revision}.${this.subRevision} | ${revision}.$version")
|
||||||
|
if (revision != this.revision || version != this.subRevision) {
|
||||||
|
write.writeByte(GAME_UPDATED)
|
||||||
|
write.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
write.writeByte(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm the client got our message and is ready to start sending file requests
|
||||||
|
*/
|
||||||
|
private suspend fun acknowledge(read: ByteReadChannel, write: ByteWriteChannel): Boolean {
|
||||||
|
val opcode = read.readByte().toInt()
|
||||||
|
println("Received $opcode")
|
||||||
|
if (opcode != ACKNOWLEDGE) {
|
||||||
|
write.writeByte(REJECT_SESSION)
|
||||||
|
write.close()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return verify(read, write, ACKNOWLEDGE_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm a session value send by the client is as the server [expected]
|
||||||
|
*/
|
||||||
|
private suspend fun verify(read: ByteReadChannel, write: ByteWriteChannel, expected: Int): Boolean {
|
||||||
|
val id = read.readMedium()
|
||||||
|
if (id != expected) {
|
||||||
|
write.writeByte(BAD_SESSION_ID)
|
||||||
|
write.close()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun readRequests(read: ByteReadChannel, write: ByteWriteChannel) = coroutineScope {
|
||||||
|
try {
|
||||||
|
while (isActive) {
|
||||||
|
readRequest(read, write)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
println("Client disconnected js5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify status updates and pass requests onto the [server] to fulfill
|
||||||
|
*/
|
||||||
|
private suspend fun readRequest(read: ByteReadChannel, write: ByteWriteChannel) {
|
||||||
|
when (val opcode = read.readByte().toInt()) {
|
||||||
|
STATUS_LOGGED_OUT, STATUS_LOGGED_IN -> verify(read, write, STATUS_ID)
|
||||||
|
PRIORITY_REQUEST, PREFETCH_REQUEST -> server.fulfill(read, write, opcode == PREFETCH_REQUEST)
|
||||||
|
else -> {
|
||||||
|
println("Closing write")
|
||||||
|
write.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
running = false
|
||||||
|
dispatcher.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// Session ids
|
||||||
|
const val ACKNOWLEDGE_ID = 3
|
||||||
|
const val STATUS_ID = 0
|
||||||
|
|
||||||
|
// Opcodes
|
||||||
|
const val PREFETCH_REQUEST = 0
|
||||||
|
const val PRIORITY_REQUEST = 1
|
||||||
|
const val HANDSHAKE_REQUEST = 15
|
||||||
|
const val STATUS_LOGGED_IN = 2
|
||||||
|
const val STATUS_LOGGED_OUT = 3
|
||||||
|
const val ACKNOWLEDGE = 6
|
||||||
|
|
||||||
|
// Response codes
|
||||||
|
private const val GAME_UPDATED = 6
|
||||||
|
private const val BAD_SESSION_ID = 10
|
||||||
|
private const val REJECT_SESSION = 11
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package js5server
|
||||||
|
|
||||||
|
import com.displee.cache.CacheLibrary
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
object JS5Server {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
|
val file = File("./file-server.properties")
|
||||||
|
if (!file.exists()) {
|
||||||
|
System.err.println("Unable to find server properties file.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Start up...")
|
||||||
|
|
||||||
|
var revision = 530
|
||||||
|
var subRevision = 1
|
||||||
|
var port = 43593
|
||||||
|
var threads = 0
|
||||||
|
lateinit var cachePath: String
|
||||||
|
file.forEachLine { line ->
|
||||||
|
val (key, value) = line.split("=")
|
||||||
|
when (key) {
|
||||||
|
"revision" -> revision = value.toInt()
|
||||||
|
"subrevision" -> subRevision = value.toInt()
|
||||||
|
"port" -> port = value.toInt()
|
||||||
|
"threads" -> threads = value.toInt()
|
||||||
|
"cachePath" -> cachePath = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Loaded configuration.")
|
||||||
|
|
||||||
|
val cache = CacheLibrary(cachePath)
|
||||||
|
val versionTable = cache.generateOldUkeys()
|
||||||
|
val fileServer = FileServer(DataProvider(cache), versionTable)
|
||||||
|
|
||||||
|
println("Loaded cache revision $revision from $cachePath")
|
||||||
|
|
||||||
|
val network = JS5Net(fileServer, revision, subRevision)
|
||||||
|
|
||||||
|
println("Loading complete [${System.currentTimeMillis() - start}ms] / bound to 127.0.0.1:$port")
|
||||||
|
|
||||||
|
val runtime = Runtime.getRuntime()
|
||||||
|
runtime.addShutdownHook(thread(start = false) { network.stop() })
|
||||||
|
network.start(port, threads)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package js5server.ext
|
||||||
|
|
||||||
|
import io.ktor.utils.io.*
|
||||||
|
import io.ktor.utils.io.core.*
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import kotlin.text.toByteArray
|
||||||
|
|
||||||
|
private val CHARSET = charset("UTF-8")
|
||||||
|
|
||||||
|
suspend fun ByteReadChannel.readMedium(): Int {
|
||||||
|
return (readByte().toInt() shl 16) + (readByte().toInt() shl 8) + readByte().toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun ByteReadChannel.readUByte(): Int {
|
||||||
|
return readByte().toInt() and 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun ByteReadChannel.readUMedium(): Int {
|
||||||
|
return (readUByte() shl 16) + (readUByte() shl 8) + readUByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteBuffer.putSmart(value: Int) {
|
||||||
|
if (value >= 128) {
|
||||||
|
putShort((value + 32768).toShort())
|
||||||
|
} else {
|
||||||
|
put(value.toByte())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteBuffer.putJagexString(string: String) {
|
||||||
|
put(0)
|
||||||
|
put(string.toByteArray(CHARSET))
|
||||||
|
put(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteBuffer.putShortA(value: Int) {
|
||||||
|
put(((value shr 8) and 0xFFFF).toByte())
|
||||||
|
put(((value + 128) and 0xFF).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteBuffer.putIntB(value: Int) {
|
||||||
|
put((value shr 16).toByte())
|
||||||
|
put((value shr 24).toByte())
|
||||||
|
put((value).toByte())
|
||||||
|
put((value shr 8).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteBuffer.putUnsignedByteS(value: Byte) {
|
||||||
|
put(((value + 128) and 0xFF).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteBuffer.putUnsignedShort(value: Int) {
|
||||||
|
putShort(((value) and 0xFFFF).toShort())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BytePacketBuilder.writeUnsignedByteSubtract(value: Int) {
|
||||||
|
writeByte(((value + 128) and 0xFF).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BytePacketBuilder.writeUnsignedShort(value: Int) {
|
||||||
|
writeShort(((value) and 0xFFFF).toShort())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BytePacketBuilder.writeShortAdd(value: Int) {
|
||||||
|
writeByte((value shr 8).toByte())
|
||||||
|
writeByte((value + 128).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BytePacketBuilder.writeSmart(value: Int) {
|
||||||
|
if (value >= 128) {
|
||||||
|
writeShort((value + 32768).toShort())
|
||||||
|
} else {
|
||||||
|
writeByte(value.toByte())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BytePacketBuilder.writeIntME(value: Int) {
|
||||||
|
writeByte((value shr 16).toByte())
|
||||||
|
writeByte((value shr 24).toByte())
|
||||||
|
writeByte(value.toByte())
|
||||||
|
writeByte((value shr 8).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BytePacketBuilder.writeVersionedString(value: String) {
|
||||||
|
writeByte(0)
|
||||||
|
writeText(value)
|
||||||
|
writeByte(0)
|
||||||
|
}
|
||||||
@@ -3,16 +3,15 @@ package core.game.node.entity.skill.cooking
|
|||||||
import core.game.node.`object`.GameObject
|
import core.game.node.`object`.GameObject
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.item.Item
|
import core.game.node.item.Item
|
||||||
import core.plugin.Initializable
|
|
||||||
import org.rs09.consts.Items
|
import org.rs09.consts.Items
|
||||||
import org.rs09.consts.Items.BREAD_DOUGH_2307
|
import org.rs09.consts.Items.BREAD_DOUGH_2307
|
||||||
import org.rs09.consts.Items.RAW_BEEF_2132
|
import org.rs09.consts.Items.RAW_BEEF_2132
|
||||||
import org.rs09.consts.Items.SEAWEED_401
|
import org.rs09.consts.Items.SEAWEED_401
|
||||||
import org.rs09.consts.Items.UNCOOKED_CAKE_1889
|
import org.rs09.consts.Items.UNCOOKED_CAKE_1889
|
||||||
import rs09.game.interaction.InteractionListener
|
import rs09.game.interaction.InteractionListener
|
||||||
|
import rs09.game.node.entity.skill.cooking.CookingDialogue
|
||||||
|
|
||||||
//author: Ceik
|
//author: Ceik
|
||||||
@Initializable
|
|
||||||
class CookingRewrite : InteractionListener() {
|
class CookingRewrite : InteractionListener() {
|
||||||
|
|
||||||
val RAW_FOODS: IntArray
|
val RAW_FOODS: IntArray
|
||||||
@@ -31,11 +30,11 @@ class CookingRewrite : InteractionListener() {
|
|||||||
val range = obj.name.toLowerCase().contains("range")
|
val range = obj.name.toLowerCase().contains("range")
|
||||||
when (item.id) {
|
when (item.id) {
|
||||||
RAW_BEEF_2132 -> if (range) {
|
RAW_BEEF_2132 -> if (range) {
|
||||||
player.dialogueInterpreter.open(FoodCookingDialogue.DialogueID, item, 9436, true, obj)
|
player.dialogueInterpreter.open(CookingDialogue(item,9436,true,obj))
|
||||||
return@onUseWith true
|
return@onUseWith true
|
||||||
}
|
}
|
||||||
SEAWEED_401 -> if (range) {
|
SEAWEED_401 -> if (range) {
|
||||||
player.dialogueInterpreter.open(FoodCookingDialogue.DialogueID, item, 1781, false, obj)
|
player.dialogueInterpreter.open(CookingDialogue(item,1781,false,obj))
|
||||||
return@onUseWith true
|
return@onUseWith true
|
||||||
}
|
}
|
||||||
BREAD_DOUGH_2307, UNCOOKED_CAKE_1889 -> if (!range) {
|
BREAD_DOUGH_2307, UNCOOKED_CAKE_1889 -> if (!range) {
|
||||||
@@ -45,7 +44,7 @@ class CookingRewrite : InteractionListener() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//cook a standard item
|
//cook a standard item
|
||||||
player.dialogueInterpreter.open(FoodCookingDialogue.DialogueID, item.id, obj)
|
player.dialogueInterpreter.open(CookingDialogue(item.id,obj))
|
||||||
return@onUseWith true
|
return@onUseWith true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
package core.game.node.entity.skill.cooking;
|
|
||||||
|
|
||||||
import core.cache.def.impl.ItemDefinition;
|
|
||||||
import core.game.content.dialogue.DialoguePlugin;
|
|
||||||
import core.game.node.entity.player.Player;
|
|
||||||
import core.game.node.entity.player.link.RunScript;
|
|
||||||
import core.game.node.object.GameObject;
|
|
||||||
import core.net.packet.PacketRepository;
|
|
||||||
import core.net.packet.context.ChildPositionContext;
|
|
||||||
import core.net.packet.out.RepositionChild;
|
|
||||||
|
|
||||||
public class FoodCookingDialogue extends DialoguePlugin {
|
|
||||||
public static final int DialogueID = 1238503;
|
|
||||||
private int initial,product;
|
|
||||||
private boolean sinew = false;
|
|
||||||
private GameObject object;
|
|
||||||
|
|
||||||
public FoodCookingDialogue(){
|
|
||||||
/**
|
|
||||||
* Empty
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
public FoodCookingDialogue(Player player){super(player);}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DialoguePlugin newInstance(Player player) {
|
|
||||||
return new FoodCookingDialogue(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean open(Object... args) {
|
|
||||||
stage = 0;
|
|
||||||
switch(args.length){
|
|
||||||
case 2:
|
|
||||||
initial = (int) args[0];
|
|
||||||
if(CookableItems.intentionalBurn(initial)){ // checks intentional burning
|
|
||||||
product = CookableItems.getIntentionalBurn(initial).getId();
|
|
||||||
} else {
|
|
||||||
product = CookableItems.forId(initial).cooked;
|
|
||||||
|
|
||||||
}
|
|
||||||
object = (GameObject) args[1];
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
initial = (int) args[0];
|
|
||||||
product = (int) args[1];
|
|
||||||
sinew = (boolean) args[2];
|
|
||||||
object = (GameObject) args[3];
|
|
||||||
if(sinew){
|
|
||||||
player.getDialogueInterpreter().sendOptions("Select one","Dry the meat into sinew","Cook the meat");
|
|
||||||
stage = 100;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
display();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handle(int interfaceId, int buttonId) {
|
|
||||||
switch(stage){
|
|
||||||
case 0:
|
|
||||||
end();
|
|
||||||
int amount = getAmount(buttonId);
|
|
||||||
switch(amount){
|
|
||||||
case -1:
|
|
||||||
player.setAttribute("runscript", new RunScript() {
|
|
||||||
@Override
|
|
||||||
public boolean handle() {
|
|
||||||
int amount = (int) value;
|
|
||||||
CookingRewrite.cook(player, object, initial, product, amount);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
player.getDialogueInterpreter().sendInput(false,"Enter an amount:");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
end();
|
|
||||||
CookingRewrite.cook(player,object,initial,product,amount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 100:
|
|
||||||
switch(buttonId){
|
|
||||||
case 1:
|
|
||||||
CookingRewrite.cook(player,object,initial,product,1);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
product = CookableItems.forId(initial).cooked;
|
|
||||||
display();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int getAmount(final int buttonId) {
|
|
||||||
switch (buttonId) {
|
|
||||||
case 5:
|
|
||||||
return 1;
|
|
||||||
case 4:
|
|
||||||
return 5;
|
|
||||||
case 3:
|
|
||||||
return -1;
|
|
||||||
case 2:
|
|
||||||
return player.getInventory().getAmount(initial);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void display() {
|
|
||||||
player.getInterfaceManager().openChatbox(307);
|
|
||||||
PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 307, 3, 60, 90));
|
|
||||||
PacketRepository.send(RepositionChild.class, new ChildPositionContext(player,307,2,208,20));
|
|
||||||
player.getPacketDispatch().sendItemZoomOnInterface(product, 160, 307, 2);
|
|
||||||
player.getPacketDispatch().sendString(ItemDefinition.forId(product).getName(), 307, 3);
|
|
||||||
stage = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] getIds() {
|
|
||||||
return new int[] {DialogueID};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package rs09.game.node.entity.skill.cooking
|
||||||
|
|
||||||
|
import core.cache.def.impl.ItemDefinition
|
||||||
|
import core.game.node.`object`.GameObject
|
||||||
|
import core.game.node.entity.player.link.RunScript
|
||||||
|
import core.game.node.entity.skill.cooking.CookableItems
|
||||||
|
import core.game.node.entity.skill.cooking.CookingRewrite.Companion.cook
|
||||||
|
import core.net.packet.PacketRepository
|
||||||
|
import core.net.packet.context.ChildPositionContext
|
||||||
|
import core.net.packet.out.RepositionChild
|
||||||
|
import rs09.game.content.dialogue.DialogueFile
|
||||||
|
import rs09.tools.START_DIALOGUE
|
||||||
|
|
||||||
|
class CookingDialogue(vararg val args: Any) : DialogueFile(){
|
||||||
|
var initial = 0
|
||||||
|
var product = 0
|
||||||
|
var `object`: GameObject? = null
|
||||||
|
var sinew = false
|
||||||
|
override fun handle(componentID: Int, buttonID: Int) {
|
||||||
|
when(stage){
|
||||||
|
START_DIALOGUE -> {
|
||||||
|
when (args.size) {
|
||||||
|
2 -> {
|
||||||
|
initial = args.get(0) as Int
|
||||||
|
if (CookableItems.intentionalBurn(initial)) { // checks intentional burning
|
||||||
|
product = CookableItems.getIntentionalBurn(initial).id
|
||||||
|
} else {
|
||||||
|
product = CookableItems.forId(initial).cooked
|
||||||
|
}
|
||||||
|
`object` = args.get(1) as GameObject
|
||||||
|
}
|
||||||
|
4 -> {
|
||||||
|
initial = args.get(0) as Int
|
||||||
|
product = args.get(1) as Int
|
||||||
|
sinew = args.get(2) as Boolean
|
||||||
|
`object` = args.get(3) as GameObject
|
||||||
|
if (sinew) {
|
||||||
|
player!!.dialogueInterpreter.sendOptions(
|
||||||
|
"Select one",
|
||||||
|
"Dry the meat into sinew",
|
||||||
|
"Cook the meat"
|
||||||
|
)
|
||||||
|
stage = 100
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display()
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
end()
|
||||||
|
val amount = getAmount(buttonID)
|
||||||
|
when (amount) {
|
||||||
|
-1 -> {
|
||||||
|
player!!.setAttribute("runscript", object : RunScript() {
|
||||||
|
override fun handle(): Boolean {
|
||||||
|
val amount = value as Int
|
||||||
|
cook(player, `object`, initial, product, amount)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
player!!.dialogueInterpreter.sendInput(false, "Enter an amount:")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
end()
|
||||||
|
cook(player!!, `object`, initial, product, amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
100 -> {
|
||||||
|
when (buttonID) {
|
||||||
|
1 -> cook(player!!, `object`, initial, product, 1)
|
||||||
|
2 -> {
|
||||||
|
product = CookableItems.forId(initial).cooked
|
||||||
|
display()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAmount(buttonId: Int): Int {
|
||||||
|
when (buttonId) {
|
||||||
|
5 -> return 1
|
||||||
|
4 -> return 5
|
||||||
|
3 -> return -1
|
||||||
|
2 -> return player!!.inventory.getAmount(initial)
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun display() {
|
||||||
|
player!!.interfaceManager.openChatbox(307)
|
||||||
|
PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 3, 60, 90))
|
||||||
|
PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 2, 208, 20))
|
||||||
|
player!!.packetDispatch.sendItemZoomOnInterface(product, 160, 307, 2)
|
||||||
|
player!!.packetDispatch.sendString(ItemDefinition.forId(product).name, 307, 3)
|
||||||
|
stage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
rootProject.name = 'RS09'
|
rootProject.name = 'RS09'
|
||||||
include 'Client'
|
include 'Client'
|
||||||
include 'Management-Server'
|
include 'Management-Server'
|
||||||
include 'Server'
|
include 'Server'
|
||||||
|
include 'File-Server'
|
||||||
Reference in New Issue
Block a user