Skip to content

Commit

Permalink
Add useful game.properties (#460)
Browse files Browse the repository at this point in the history
* Add double item box continuation closes #459

* Add script packages to game.properties closes #456

* Exit server on BindException closes #431

* Add experience rate multiplier closes #453

* Add gradually spawning bots on startup closes #437

* Remove normal player commands as console blocks them

* Add automatic admin property closes #454

* Add file server disabling #407

* Fix hunt mode helpers
  • Loading branch information
GregHib committed Feb 24, 2024
1 parent d491ca6 commit a4ecaba
Show file tree
Hide file tree
Showing 23 changed files with 191 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ val engineModule = module {
single {
PlayerAccounts(get(), get(), get(), get(), get(), get(), getProperty("savePath"), get(), get(), Tile(
getIntProperty("homeX", 0), getIntProperty("homeY", 0), getIntProperty("homeLevel", 0)
))
), getProperty("experienceRate", "1.0").toDouble())
}
// IO
single { Yaml(YamlReaderConfiguration(2, 8, VERY_FAST_LOAD_FACTOR)) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ data class Command(
override fun copy(approach: Boolean) = copy().apply { this.approach = approach }
}

fun command(vararg commands: String, block: suspend Command.() -> Unit) {
for (command in commands) {
on<Command>({ wildcardEquals(command, prefix) }) { _: Player ->
block.invoke(this)
}
}
}

fun adminCommand(vararg commands: String, block: suspend Command.() -> Unit) {
for (command in commands) {
on<Command>({ wildcardEquals(command, prefix) && it.isAdmin() }) { _: Player ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ class PlayerAccounts(
private val path: String,
private val collisionStrategyProvider: CollisionStrategyProvider,
private val variableDefinitions: VariableDefinitions,
private val homeTile: Tile
private val homeTile: Tile,
experienceRate: Double
) : Runnable {

private val validItems = ValidItemRestriction(itemDefinitions)
private val writeConfig = PlayerYamlWriterConfig()
private val readerConfig = PlayerYamlReaderConfig(itemDefinitions)
private val readerConfig = PlayerYamlReaderConfig(itemDefinitions, experienceRate)
private val saveQueue = mutableMapOf<String, PlayerSave>()

private fun path(name: String) = "$path${name}.json"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import world.gregs.voidps.type.Tile
import world.gregs.yaml.read.YamlReaderConfiguration

internal class PlayerYamlReaderConfig(
private val itemDefinitions: ItemDefinitions
private val itemDefinitions: ItemDefinitions,
private val experienceRate: Double
) : YamlReaderConfiguration() {
override fun add(list: MutableList<Any>, value: Any, parentMap: String?) {
if (value is Map<*, *> && value.containsKey("id")) {
Expand All @@ -35,7 +36,8 @@ internal class PlayerYamlReaderConfig(
value as Map<String, Any>
val exp = Experience(
experience = (value["experience"] as List<Double>).toDoubleArray(),
blocked = (value["blocked"] as List<Skill>).toMutableSet()
blocked = (value["blocked"] as List<Skill>).toMutableSet(),
rate = experienceRate
)
super.set(map, key, exp, indent, parentMap)
} else if (key == "levels") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fun huntNPC(npc: String = "*", targetNpc: String = "*", mode: String = "*", bloc
on<HuntNPC>({ wildcardEquals(npc, it.id) && wildcardEquals(targetNpc, target.id) && wildcardEquals(mode, this.mode) }, block = block)
}

fun huntNPC(vararg modes: String, block: suspend HuntNPC.(npc: NPC) -> Unit) {
fun huntNPCModes(vararg modes: String, block: suspend HuntNPC.(npc: NPC) -> Unit) {
for(mode in modes) {
on<HuntNPC>({ wildcardEquals(mode, this.mode) }, block = block)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fun huntPlayer(npc: String = "*", mode: String = "*", block: suspend HuntPlayer.
on<HuntPlayer>({ wildcardEquals(npc, it.id) && wildcardEquals(mode, this.mode) }, block = block)
}

fun huntPlayer(vararg modes: String, block: suspend HuntPlayer.(npc: NPC) -> Unit) {
fun huntPlayerModes(vararg modes: String, block: suspend HuntPlayer.(npc: NPC) -> Unit) {
for (mode in modes) {
on<HuntPlayer>({ wildcardEquals(mode, this.mode) }, block = block)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import world.gregs.voidps.engine.event.Events
class Experience(
val experience: DoubleArray = defaultExperience.clone(),
val blocked: MutableSet<Skill> = mutableSetOf(),
private val maximum: Double = MAXIMUM_EXPERIENCE
private val maximum: Double = MAXIMUM_EXPERIENCE,
private val rate: Double = DEFAULT_EXPERIENCE_RATE
) {

lateinit var events: Events
Expand All @@ -35,10 +36,10 @@ class Experience(
return
}
if (blocked.contains(skill)) {
events.emit(BlockedExperience(skill, experience))
events.emit(BlockedExperience(skill, experience * rate))
} else {
val current = get(skill)
set(skill, current + experience)
set(skill, current + experience * rate)
}
}

Expand All @@ -53,6 +54,7 @@ class Experience(
}

companion object {
const val DEFAULT_EXPERIENCE_RATE = 1.0
const val MAXIMUM_EXPERIENCE = 200000000.0
val defaultExperience = DoubleArray(Skill.count) {
if (it == Skill.Constitution.ordinal) 1154.0 else 0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ internal class ExperienceTest {
assertEquals(20.0, experience.get(Skill.Attack))
}

@Test
fun `Add experience with 10x rate`() {
experience = Experience(maximum = 500.0, rate = 10.0)
experience.events = events
experience.add(Skill.Attack, 10.0)
experience.add(Skill.Attack, 10.0)
assertEquals(200.0, experience.get(Skill.Attack))
}

@Test
fun `Can't set negative experience`() {
experience.set(Skill.Attack, -10.0)
Expand Down
12 changes: 10 additions & 2 deletions game/src/main/kotlin/world/gregs/voidps/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import world.gregs.voidps.network.LoginServer
import world.gregs.voidps.network.protocol
import world.gregs.voidps.script.loadScripts
import java.io.File
import java.net.BindException
import java.util.*

/**
Expand Down Expand Up @@ -61,8 +62,15 @@ object Main {
World.start(properties)
engine.start()

logger.info { "$name loaded in ${System.currentTimeMillis() - startTime}ms" }
server.start(getIntProperty("port"))
try {
server.start(getIntProperty("port"))
logger.info { "$name loaded in ${System.currentTimeMillis() - startTime}ms" }
} catch (e: BindException) {
logger.error(e) { "Error starting server." }
} finally {
server.stop()
engine.stop()
}
}

private fun properties(): Properties = timed("properties") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,67 +1,71 @@
package world.gregs.voidps.world.command.admin
package world.gregs.voidps.bot

import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import world.gregs.voidps.bot.Bot
import world.gregs.voidps.bot.StartBot
import world.gregs.voidps.bot.TaskManager
import world.gregs.voidps.bot.isBot
import kotlinx.coroutines.*
import world.gregs.voidps.engine.Contexts
import world.gregs.voidps.engine.client.ConnectionGatekeeper
import world.gregs.voidps.engine.client.ConnectionQueue
import world.gregs.voidps.engine.client.ui.event.adminCommand
import world.gregs.voidps.engine.data.PlayerAccounts
import world.gregs.voidps.engine.data.definition.AreaDefinitions
import world.gregs.voidps.engine.data.definition.EnumDefinitions
import world.gregs.voidps.engine.data.definition.StructDefinitions
import world.gregs.voidps.engine.entity.World
import world.gregs.voidps.engine.entity.character.move.running
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.entity.character.player.appearance
import world.gregs.voidps.engine.entity.character.player.sex
import world.gregs.voidps.engine.entity.worldSpawn
import world.gregs.voidps.engine.event.Event
import world.gregs.voidps.engine.event.EventHandlerStore
import world.gregs.voidps.engine.get
import world.gregs.voidps.engine.getIntProperty
import world.gregs.voidps.engine.inject
import world.gregs.voidps.engine.inv.add
import world.gregs.voidps.engine.inv.inventory
import world.gregs.voidps.engine.suspend.pause
import world.gregs.voidps.engine.timer.toTicks
import world.gregs.voidps.engine.timer.worldTimerStart
import world.gregs.voidps.engine.timer.worldTimerTick
import world.gregs.voidps.network.client.DummyClient
import world.gregs.voidps.network.visual.update.player.BodyColour
import world.gregs.voidps.network.visual.update.player.BodyPart
import world.gregs.voidps.type.area.Rectangle
import world.gregs.voidps.type.random
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.reflect.KClass

val bots = mutableListOf<Player>()
val areas: AreaDefinitions by inject()
val lumbridge = areas["lumbridge_teleport"]
val botCount = getIntProperty("bots", 0)

val bots = mutableListOf<Player>()
val queue: ConnectionQueue by inject()
val gatekeeper: ConnectionGatekeeper by inject()
val accounts: PlayerAccounts by inject()
val enums: EnumDefinitions by inject()
val structs: StructDefinitions by inject()

adminCommand("bot") {
if (player.isBot) {
player.clear("bot")
} else {
val bot = player.initBot()
if (content.isNotBlank()) {
player["task"] = content
}
bot.events.emit(StartBot)
var counter = 0

worldSpawn {
if (botCount > 0) {
World.timers.start("bot_spawn")
}
}

var counter = 0
worldTimerStart("bot_spawn") {
interval = TimeUnit.MINUTES.toTicks(1)
}

worldTimerTick("bot_spawn") {
if (counter > botCount) {
cancel()
return@worldTimerTick
}
spawn()
}

adminCommand("bots") {
val count = content.toIntOrNull() ?: 1
val lumbridge = Rectangle(3221, 3217, 3222, 3220)
val tile = lumbridge.random()
GlobalScope.launch {
repeat(count) {
if (it % 25 == 0) {
Expand All @@ -70,32 +74,48 @@ adminCommand("bots") {
cont.resume(Unit)
}
}
}
GlobalScope.launch(Contexts.Game) {
val name = "Bot ${++counter}"
val index = gatekeeper.connect(name)!!
val bot = accounts.getOrElse(name, index) { Player(index = index, tile = tile, accountName = name) }
setAppearance(bot)
queue.await()
if (bot.inventory.isEmpty()) {
bot.inventory.add("coins", 10000)
}
val client = if (TaskManager.DEBUG) DummyClient() else null
bot.initBot()
bot.login(client, 0)
bot.events.emit(StartBot)
bot.viewport?.loaded = true
pause(3)
bots.add(bot)
bot.running = true
spawn()
}
}
}
}

adminCommand("bot") {
if (player.isBot) {
player.clear("bot")
} else {
val bot = player.initBot()
if (content.isNotBlank()) {
player["task"] = content
}
bot.events.emit(StartBot)
}
}

fun spawn() {
GlobalScope.launch(Contexts.Game) {
val name = "Bot ${++counter}"
val index = gatekeeper.connect(name)!!
val bot = accounts.getOrElse(name, index) { Player(index = index, tile = lumbridge.random(), accountName = name) }
setAppearance(bot)
queue.await()
if (bot.inventory.isEmpty()) {
bot.inventory.add("coins", 10000)
}
val client = if (TaskManager.DEBUG) DummyClient() else null
bot.initBot()
bot.login(client, 0)
bot.events.emit(StartBot)
bot.viewport?.loaded = true
delay(3)
bots.add(bot)
bot.running = true
}
}

fun Player.initBot(): Bot {
val bot = Bot(this)
get<EventHandlerStore>().populate(Bot::class, bot.botEvents)
world.gregs.voidps.engine.get<EventHandlerStore>().populate(Bot::class, bot.botEvents)
this["bot"] = bot
val e = ConcurrentLinkedQueue<Event>()
this["events"] = e
Expand Down Expand Up @@ -138,4 +158,4 @@ fun setAppearance(player: Player): Player {
player.body.setColour(BodyColour.Skin, enums.get("character_skin").randomInt())
player.appearance.emote = 1426
return player
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ fun Bot.hasExactGear(gear: GearDefinition): Boolean {
}

private suspend fun Bot.setupGearAndInv(gear: GearDefinition, buy: Boolean) {
println("Setup $gear")
// Pick one of each item to equip for each required slot
for ((_, equipmentList) in gear.equipment) {
val items = equipmentList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import com.github.michaelbull.logging.InlineLogger
import io.github.classgraph.ClassGraph
import world.gregs.voidps.Main
import world.gregs.voidps.engine.client.ui.chat.plural
import world.gregs.voidps.engine.getProperty
import kotlin.system.measureTimeMillis

private val logger = InlineLogger("ScriptLoader")

fun loadScripts() {
fun loadScripts(scriptPackage: String = getProperty("scriptPackage"), botScriptPackage: String = getProperty("botScriptPackage")) {
var scriptCount = 0
val found = mutableSetOf<String>()
val isJar = Main::class.java.getResource("${Main::class.simpleName}.class")?.protocol == "jar"
val arguments = emptyArray<String>()
val time = measureTimeMillis {
ClassGraph()
.filterClasspathElements { isJar || !it.endsWith(".jar") }
.acceptPackages("world.gregs.voidps.world", "world.gregs.voidps.bot")
.acceptPackages(scriptPackage, botScriptPackage)
.enableMethodInfo()
.scan().use { scanResult ->
for (info in scanResult.allClasses) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import world.gregs.voidps.engine.client.ui.chat.toSILong
import world.gregs.voidps.engine.client.ui.chat.toSIPrefix
import world.gregs.voidps.engine.client.ui.close
import world.gregs.voidps.engine.client.ui.event.adminCommand
import world.gregs.voidps.engine.client.ui.event.command
import world.gregs.voidps.engine.client.ui.event.modCommand
import world.gregs.voidps.engine.client.ui.open
import world.gregs.voidps.engine.client.ui.playTrack
Expand Down Expand Up @@ -123,7 +122,7 @@ adminCommand("npc") {
npc?.start("movement_delay", -1)
}

command("save") {
modCommand("save") {
val account: PlayerAccounts = get()
players.forEach(account::queueSave)
}
Expand Down
Loading

0 comments on commit a4ecaba

Please sign in to comment.