Random event manager rewrite

Time until next random event now persists across logins
Events will no longer spawn while the player has an interface like the bank open
Replaced the slurry of random event commands with a singular ::revent admin command
Random events now support being associated with multiple skills
Random event selection is now more robust
Players will no longer get skill-based random events if more than 2.5 minutes have passed since training the skill
This commit is contained in:
Ceikry
2023-08-14 12:56:35 +00:00
committed by Ryan
parent bb21ad533b
commit a3e6df109d
26 changed files with 538 additions and 285 deletions
+58 -18
View File
@@ -1,29 +1,31 @@
import content.global.ame.RandomEventManager
import content.global.skill.farming.timers.CropGrowth
import core.ServerConstants
import core.api.log
import core.cache.Cache
import core.cache.crypto.ISAACCipher
import core.cache.crypto.ISAACPair
import core.game.node.entity.player.Player
import core.game.node.entity.player.info.PlayerDetails
import core.game.node.entity.player.info.Rights
import core.game.node.entity.player.link.IronmanMode
import core.game.node.item.Item
import core.net.IoSession
import core.net.packet.IoBuffer
import org.rs09.consts.Items
import core.ServerConstants
import core.api.log
import core.game.interaction.ScriptProcessor
import core.game.node.Node
import core.game.node.entity.combat.equipment.WeaponInterface
import core.game.node.entity.npc.NPC
import core.game.node.entity.skill.SkillBonus
import core.game.node.entity.player.Player
import core.game.node.entity.player.VarpManager
import core.game.node.entity.player.info.PlayerDetails
import core.game.node.entity.player.info.Rights
import core.game.node.entity.player.info.login.PlayerSaveParser
import core.game.node.entity.player.info.login.PlayerSaver
import core.game.node.entity.player.link.IronmanMode
import core.game.node.entity.player.link.SavedData
import core.game.node.entity.player.link.quest.QuestRepository
import core.game.node.entity.skill.Skills
import core.game.node.item.GroundItem
import core.game.node.item.Item
import core.game.shops.Shop
import core.game.shops.ShopItem
import core.tools.SystemLogger
import core.game.system.config.ConfigParser
import core.game.system.config.ServerConfigParser
import core.game.system.timer.TimerRegistry
import core.game.system.timer.impl.AntiMacro
import core.game.system.timer.impl.Disease
import core.game.system.timer.impl.Poison
import core.game.world.GameWorld
@@ -31,10 +33,11 @@ import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.repository.Repository
import core.game.world.update.UpdateSequence
import core.net.IoSession
import core.net.packet.IoBuffer
import core.net.packet.PacketProcessor
import core.tools.Log
import org.rs09.consts.Scenery
import java.io.Closeable
import org.rs09.consts.Items
import java.net.URI
import java.nio.ByteBuffer
@@ -85,6 +88,7 @@ object TestUtils {
TimerRegistry.registerTimer(Poison())
TimerRegistry.registerTimer(Disease())
TimerRegistry.registerTimer(CropGrowth())
TimerRegistry.registerTimer(AntiMacro())
}
fun loadFile(path: String) : URI? {
@@ -111,12 +115,17 @@ object TestUtils {
class MockPlayer(name: String) : Player(PlayerDetails(name)), AutoCloseable {
var hasInit = false
init {
init { configureBasicProperties(); flagTutComplete(false); init(); flagTutComplete(true) }
fun configureBasicProperties() {
this.details.session = MockSession()
this.location = ServerConstants.HOME_LOCATION
this.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_CRUSH)
init()
this.setAttribute("tutorial:complete", true)
}
fun flagTutComplete(complete: Boolean) {
this.setAttribute("/save:tutorial:complete", complete)
this.setAttribute("/save:rules:confirmed", complete)
}
override fun update() {
@@ -140,6 +149,37 @@ class MockPlayer(name: String) : Player(PlayerDetails(name)), AutoCloseable {
RegionManager.move(this)
this.playerFlags.lastSceneGraph = location
}
fun relog(ticksToWait: Int = -1) {
val json = PlayerSaver(this).populate()
val parse = PlayerSaveParser(this)
parse.saveFile = json
close()
timers.clearTimers()
inventory.clear()
bank.clear()
equipment.clear()
skills = Skills(this)
savedData = SavedData(this)
questRepository = QuestRepository(this)
varpManager = VarpManager(this)
varpMap.clear()
saveVarp.clear()
scripts = ScriptProcessor(this)
clearAttributes()
hasInit = false
if (ticksToWait > 0) TestUtils.advanceTicks(ticksToWait, false)
configureBasicProperties()
parse.parseData()
init()
}
override fun debug(string: String?) {
log (this::class.java, Log.DEBUG, "[$name] -> Received Debug: $string")
}
}
class MockSession : IoSession(null, null) {
@@ -1,81 +0,0 @@
package content
import TestUtils
import content.global.ame.RandomEventManager
import core.game.node.item.Item
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
import content.global.ame.RandomEventNPC
import core.game.world.GameWorld
class RandomEventManagerTests {
init {
TestUtils.preTestSetup()
}
@Test fun loginShouldEnableManager() {
val p = TestUtils.getMockPlayer("Bill")
RandomEventManager().login(p)
val manager = content.global.ame.RandomEventManager.getInstance(p)
Assertions.assertNotNull(manager)
Assertions.assertEquals(true, manager!!.enabled)
}
@Test fun loginShouldSetNextSpawn() {
val p = TestUtils.getMockPlayer("Bill")
RandomEventManager().login(p)
val manager = content.global.ame.RandomEventManager.getInstance(p)
Assertions.assertNotNull(manager)
Assertions.assertEquals(true, manager!!.nextSpawn > GameWorld.ticks)
}
@Test fun shouldSpawnRandomEventWithinMAXTICKSGivenNoRestrictions() {
val p = TestUtils.getMockPlayer("Bill")
p.setAttribute("tutorial:complete", true) //tutorial has to be complete to spawn randoms
RandomEventManager.MIN_DELAY_TICKS = 10
RandomEventManager.MAX_DELAY_TICKS = 20
RandomEventManager().login(p)
TestUtils.advanceTicks(RandomEventManager.MAX_DELAY_TICKS + 5)
RandomEventManager.MIN_DELAY_TICKS = 3000
RandomEventManager.MAX_DELAY_TICKS = 9000
Assertions.assertNotNull(p.getAttribute("re-npc", null))
}
@Test fun teleportAndNotePunishmentShouldNotAffectAlreadyNotedItems() {
val p = TestUtils.getMockPlayer("Shitforbrains")
p.setAttribute("tutorial:complete", true)
RandomEventManager().login(p)
p.inventory.add(Item(Items.RAW_SHARK_384, 1000))
content.global.ame.RandomEventManager.getInstance(p)?.fireEvent()
p.getAttribute<RandomEventNPC>("re-npc")!!.noteAndTeleport()
Assertions.assertEquals(1000, p.inventory.getAmount(Items.RAW_SHARK_384))
}
@Test fun teleportAndNotePunishmentShouldNoteNotableUnnotedItems() {
val p = TestUtils.getMockPlayer("shitforbrains2")
p.setAttribute("tutorial:complete", true)
RandomEventManager().login(p)
p.inventory.add(Item(4151, 5))
content.global.ame.RandomEventManager.getInstance(p)?.fireEvent()
p.getAttribute<RandomEventNPC>("re-npc")!!.noteAndTeleport()
Assertions.assertEquals(5, p.inventory.getAmount(4152))
Assertions.assertEquals(0, p.inventory.getAmount(4151))
}
@Test fun teleportAndNotePunishmentShouldNotAffectUnnotableItems() {
val p = TestUtils.getMockPlayer("shitforbrains3")
p.setAttribute("tutorial:complete", true)
RandomEventManager().login(p)
p.inventory.add(Item(Items.AIR_RUNE_556, 30))
content.global.ame.RandomEventManager.getInstance(p)?.fireEvent()
p.getAttribute<RandomEventNPC>("re-npc")!!.noteAndTeleport()
Assertions.assertEquals(30, p.inventory.getAmount(Items.AIR_RUNE_556))
}
}
@@ -0,0 +1,217 @@
package content
import TestUtils
import content.global.ame.RandomEventNPC
import content.global.ame.RandomEvents
import core.api.*
import core.game.system.timer.impl.AntiMacro
import core.game.world.map.Location
import core.game.world.map.zone.impl.WildernessZone
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
import org.rs09.consts.NPCs
class RandomEventTests {
init {
TestUtils.preTestSetup()
}
@Test fun loginShouldRegisterRandomEventTimer() {
TestUtils.getMockPlayer("antimacroAutoRegister").use { p ->
Assertions.assertNotNull(getTimer<AntiMacro>(p))
}
}
@Test fun loginShouldSetNextSpawn() {
TestUtils.getMockPlayer("antimacroautosetsnextspawnifunset").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null.")
Assertions.assertNotEquals(getWorldTicks(), timer.nextExecution)
Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() >= AntiMacro.MIN_DELAY_TICKS)
Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() <= AntiMacro.MAX_DELAY_TICKS)
}
}
@Test fun remainingDelayShouldPersistRelog() {
TestUtils.getMockPlayer("antimacrotimeremainingpersists").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null.")
timer.nextExecution = getWorldTicks() + 666
p.relog(ticksToWait = 100)
Assertions.assertEquals(getWorldTicks() + 666, getTimer<AntiMacro>(p)!!.nextExecution)
}
}
@Test fun delayShouldBeRestartedOnceDepleted() {
TestUtils.getMockPlayer("delayrestartoncedepleted").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null.")
TestUtils.advanceTicks(2, false)
timer.nextExecution = 5 //run in 5 ticks
TestUtils.advanceTicks(5, false)
Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() >= AntiMacro.MIN_DELAY_TICKS)
Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() <= AntiMacro.MAX_DELAY_TICKS)
}
}
@Test fun shouldSpawnRandomEventGivenNoRestrictions() {
TestUtils.getMockPlayer("antimacroshouldspawnrandom").use {p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNotNull(p.getAttribute(AntiMacro.EVENT_NPC, null))
Assertions.assertEquals(true, p.getAttribute<RandomEventNPC>(AntiMacro.EVENT_NPC).isActive)
}
}
@Test fun teleportAndNotePunishmentShouldNotAffectAlreadyNotedItems() {
TestUtils.getMockPlayer("teleportpunishment1").use {p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
addItem(p, Items.RAW_SHARK_384, 1000)
getAttribute<RandomEventNPC?>(p, AntiMacro.EVENT_NPC, null)!!.noteAndTeleport()
Assertions.assertEquals(1000, amountInInventory(p, Items.RAW_SHARK_384))
}
}
@Test fun teleportAndNotePunishmentShouldNoteNotableUnnotedItems() {
TestUtils.getMockPlayer("teleportpunishment2").use {p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
addItem(p, Items.ABYSSAL_WHIP_4151, 5)
getAttribute<RandomEventNPC?>(p, AntiMacro.EVENT_NPC, null)!!.noteAndTeleport()
Assertions.assertEquals(5, amountInInventory(p, Items.ABYSSAL_WHIP_4152))
Assertions.assertEquals(0, amountInInventory(p, Items.ABYSSAL_WHIP_4151))
}
}
@Test fun teleportAndNotePunishmentShouldNotAffectUnnotableItems() {
TestUtils.getMockPlayer("teleportpunishment3").use {p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer is null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
addItem(p, Items.AIR_RUNE_556, 30)
getAttribute<RandomEventNPC?>(p, AntiMacro.EVENT_NPC, null)!!.noteAndTeleport()
Assertions.assertEquals(30, amountInInventory(p, Items.AIR_RUNE_556))
}
}
@Test fun randomEventShouldNotSpawnInEventRestrictedArea() {
TestUtils.getMockPlayer("antimacronospawninrestrictedzone").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer was null!")
TestUtils.advanceTicks(5, false)
//Wilderness is Random Event restricted
WildernessZone.getInstance().configure()
val loc = Location.create(3131, 3595)
p.location = loc
Assertions.assertEquals(loc, p.location)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNull(AntiMacro.getEventNpc(p))
}
}
@Test fun randomEventShouldNotSpawnIfOneAlreadyActive() {
TestUtils.getMockPlayer("antimacronospawnifalreadyhas").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer was null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
val previousEvent = AntiMacro.getEventNpc(p)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
Assertions.assertEquals(previousEvent, AntiMacro.getEventNpc(p))
}
}
@Test fun randomEventShouldSpawnIfCurrentOneIsInactive() {
TestUtils.getMockPlayer("antimacrospawnifcurrentinactive").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer was null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
val previousEvent = AntiMacro.getEventNpc(p)
AntiMacro.terminateEventNpc(p)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
Assertions.assertNotEquals(previousEvent, AntiMacro.getEventNpc(p))
}
}
@Test fun randomEventSystemShouldSupportPauseAndUnpause() {
TestUtils.getMockPlayer("antimacropause").use { p ->
val timer = getTimer<AntiMacro>(p) ?: Assertions.fail("AntiMacro timer was null!")
TestUtils.advanceTicks(5, false)
timer.nextExecution = getWorldTicks() + 5
AntiMacro.pause(p)
TestUtils.advanceTicks(10, false)
Assertions.assertNull(AntiMacro.getEventNpc(p))
AntiMacro.unpause(p)
timer.nextExecution = getWorldTicks() + 5
TestUtils.advanceTicks(10, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
}
}
@Test fun shouldBeAbleToForceRandomEvent() {
TestUtils.getMockPlayer("antimacroforcerand").use { p ->
TestUtils.advanceTicks(5, false)
AntiMacro.forceEvent(p)
TestUtils.advanceTicks(1, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
}
}
@Test fun shouldBeAbleToForceSpecificRandomEvent() {
TestUtils.getMockPlayer("antimacroforcespecific").use { p ->
TestUtils.advanceTicks(5, false)
AntiMacro.forceEvent(p, RandomEvents.TREE_SPIRIT)
TestUtils.advanceTicks(1, false)
Assertions.assertNotNull(AntiMacro.getEventNpc(p))
Assertions.assertEquals(NPCs.TREE_SPIRIT_438, AntiMacro.getEventNpc(p)?.originalId)
}
}
@Test fun parseAntiMacroCommandArgsShouldReturnExpectedValues() {
val testData = arrayOf(
Triple("revent -p test", "test", null),
Triple("revent -p test ", "test", null),
Triple("revent -p test -e certer ", "test", RandomEvents.CERTER),
Triple("revent -p test_user -e Sandwich lady", "test_user", RandomEvents.SANDWICH_LADY),
Triple("revent -p test user -e sandwich Lady ", "test_user", RandomEvents.SANDWICH_LADY),
Triple("revent -e sandwich Lady -p test user ", "test_user", RandomEvents.SANDWICH_LADY),
Triple("revent test", "test", null),
Triple("revent test -e sAndwich Lady", "test", RandomEvents.SANDWICH_LADY)
)
for ((commandStr, expectedUser, expectedEvent) in testData) {
val (resultUser, resultEvent) = AntiMacro.parseCommandArgs(commandStr, "revent")
Assertions.assertEquals(expectedUser, resultUser)
Assertions.assertEquals(expectedEvent, resultEvent)
}
}
}