mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-10 10:20:41 -07:00
Implemented save file versioning
Players who likely bought their crafting capes back when the hood was not obtainable will be given the complementary hood Unlocked Surok's Theme for players who are eligible Fixed some bugs relating to the handling of edge cases for random events Fixed tutorial island quest completion Made it possible to collapse interface stones in resizable HD
This commit is contained in:
parent
2776270c7f
commit
49a10d192a
14 changed files with 132 additions and 28 deletions
|
|
@ -11,7 +11,7 @@ import org.rs09.consts.NPCs
|
||||||
|
|
||||||
object DrillDemonUtils {
|
object DrillDemonUtils {
|
||||||
val DD_KEY_TASK = "/save:drilldemon:task"
|
val DD_KEY_TASK = "/save:drilldemon:task"
|
||||||
val DD_KEY_RETURN_LOC = "/save:drilldemon:original-loc"
|
val DD_KEY_RETURN_LOC = "/save:original-loc"
|
||||||
val DD_SIGN_VARP = 531
|
val DD_SIGN_VARP = 531
|
||||||
val DD_SIGN_JOG = 0
|
val DD_SIGN_JOG = 0
|
||||||
val DD_SIGN_SITUP = 1
|
val DD_SIGN_SITUP = 1
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import org.rs09.consts.NPCs
|
||||||
import org.rs09.consts.Scenery
|
import org.rs09.consts.Scenery
|
||||||
|
|
||||||
object EvilBobUtils {
|
object EvilBobUtils {
|
||||||
const val prevLocation = "/save:evilbob:prevlocation"
|
const val prevLocation = "/save:original-loc"
|
||||||
const val eventComplete = "/save:evilbob:eventcomplete"
|
const val eventComplete = "/save:evilbob:eventcomplete"
|
||||||
const val assignedFishingZone = "/save:evilbob:fishingzone"
|
const val assignedFishingZone = "/save:evilbob:fishingzone"
|
||||||
const val fishCaught = "evilbob:fishcaught"
|
const val fishCaught = "evilbob:fishcaught"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import core.tools.RandomFunction
|
||||||
|
|
||||||
object FreakUtils{
|
object FreakUtils{
|
||||||
const val freakNpc = NPCs.FREAKY_FORESTER_2458
|
const val freakNpc = NPCs.FREAKY_FORESTER_2458
|
||||||
const val freakPreviousLoc = "/save:freakyf:location"
|
const val freakPreviousLoc = "/save:original-loc"
|
||||||
const val freakTask = "/save:freakyf:task"
|
const val freakTask = "/save:freakyf:task"
|
||||||
const val freakComplete = "/save:freakyf:complete"
|
const val freakComplete = "/save:freakyf:complete"
|
||||||
const val pheasantKilled = "freakyf:killed"
|
const val pheasantKilled = "freakyf:killed"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import core.ServerConstants
|
||||||
|
|
||||||
object SurpriseExamUtils {
|
object SurpriseExamUtils {
|
||||||
|
|
||||||
val SE_KEY_LOC = "supexam:loc"
|
val SE_KEY_LOC = "/save:original-loc"
|
||||||
val SE_KEY_INDEX = "supexam:index"
|
val SE_KEY_INDEX = "supexam:index"
|
||||||
val SE_LOGOUT_KEY = "suprise_exam"
|
val SE_LOGOUT_KEY = "suprise_exam"
|
||||||
val SE_DOOR_KEY = "supexam:door"
|
val SE_DOOR_KEY = "supexam:door"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import core.game.node.item.GroundItemManager;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
import core.tools.RandomFunction;
|
import core.tools.RandomFunction;
|
||||||
|
|
||||||
|
import static core.api.ContentAPIKt.addItemOrBank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the gertrudes fortress quest.
|
* Represents the gertrudes fortress quest.
|
||||||
* @author 'Vexia
|
* @author 'Vexia
|
||||||
|
|
@ -84,7 +86,7 @@ public class GertrudesCat extends Quest {
|
||||||
player.getPacketDispatch().sendItemZoomOnInterface(kitten.getId(), 240, 277, 3 + 2);
|
player.getPacketDispatch().sendItemZoomOnInterface(kitten.getId(), 240, 277, 3 + 2);
|
||||||
setStage(player, 100);
|
setStage(player, 100);
|
||||||
if (player.getFamiliarManager().hasFamiliar()) {
|
if (player.getFamiliarManager().hasFamiliar()) {
|
||||||
player.getInventory().add(kitten);
|
addItemOrBank(player, kitten.getId(), 1);
|
||||||
} else {
|
} else {
|
||||||
player.getFamiliarManager().summon(kitten, true, false);
|
player.getFamiliarManager().summon(kitten, true, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ class ServerConstants {
|
||||||
companion object {
|
companion object {
|
||||||
var NOAUTH_DEFAULT_ADMIN: Boolean = true
|
var NOAUTH_DEFAULT_ADMIN: Boolean = true
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var CURRENT_SAVEFILE_VERSION = 1
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var DAILY_ACCOUNT_LIMIT = 3
|
var DAILY_ACCOUNT_LIMIT = 3
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,26 @@ fun addItemOrDrop(player: Player, id: Int, amount: Int = 1) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an item with a variable quantity or bank it if a player does not have enough space, or drop it if that still doesn't work
|
||||||
|
* @param player the player whose inventory to add to
|
||||||
|
* @param id the ID of the item to add to the player's inventory
|
||||||
|
* @param amount the amount of the ID to add to the player's inventory, defaults to 1
|
||||||
|
*/
|
||||||
|
fun addItemOrBank(player: Player, id: Int, amount: Int = 1) {
|
||||||
|
val item = Item(id, amount)
|
||||||
|
if (!player.inventory.add(item)) {
|
||||||
|
if (player.bankPrimary.add(item)) {
|
||||||
|
sendMessage(player, colorize("%RThe ${item.name} has been sent to your bank."))
|
||||||
|
} else if (player.bankSecondary.add(item)) {
|
||||||
|
sendMessage(player, colorize("%RThe ${item.name} has been sent to your secondary bank."))
|
||||||
|
} else {
|
||||||
|
GroundItemManager.create(item, player)
|
||||||
|
sendMessage(player, colorize("%RAs your inventory and bank account(s) are all full, the ${item.name} has been placed on the ground under your feet. Don't forget to grab it. (Also consider cleaning out some stuff, maybe? I mean, Jesus!)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs.
|
* Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs.
|
||||||
* @param npc the NPC object to initialize
|
* @param npc the NPC object to initialize
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ class Grave : AbstractNPC {
|
||||||
|
|
||||||
this.ownerUid = player.details.uid
|
this.ownerUid = player.details.uid
|
||||||
this.ownerUsername = player.username
|
this.ownerUsername = player.username
|
||||||
this.location = location
|
this.location = player.getAttribute("/save:original-loc",location)
|
||||||
this.isRespawn = false
|
this.isRespawn = false
|
||||||
this.isWalks = false
|
this.isWalks = false
|
||||||
this.isNeverWalks = true
|
this.isNeverWalks = true
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command
|
||||||
player.details.rights = Rights.REGULAR_PLAYER
|
player.details.rights = Rights.REGULAR_PLAYER
|
||||||
setAttribute(player, "tutorial:complete", true)
|
setAttribute(player, "tutorial:complete", true)
|
||||||
player.impactHandler.manualHit(player, player.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL)
|
player.impactHandler.manualHit(player, player.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL)
|
||||||
notify(player, "Grave created at ${player.location}")
|
notify(player, "Grave created at ${player.getAttribute("/save:original-loc",player.location)}")
|
||||||
GameWorld.Pulser.submit(object : Pulse(15) {
|
GameWorld.Pulser.submit(object : Pulse(15) {
|
||||||
override fun pulse(): Boolean {
|
override fun pulse(): Boolean {
|
||||||
player.details.rights = Rights.ADMINISTRATOR
|
player.details.rights = Rights.ADMINISTRATOR
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ import static core.api.utils.Permadeath.PermadeathKt.permadeath;
|
||||||
import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE;
|
import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE;
|
||||||
import static core.game.system.command.sets.StatAttributeKeysKt.STATS_DEATHS;
|
import static core.game.system.command.sets.StatAttributeKeysKt.STATS_DEATHS;
|
||||||
import static core.tools.GlobalsKt.colorize;
|
import static core.tools.GlobalsKt.colorize;
|
||||||
|
import static org.rs09.consts.Items.BONES_526;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a player entity.
|
* Represents a player entity.
|
||||||
|
|
@ -310,11 +311,18 @@ public class Player extends Entity {
|
||||||
* The amount of targets that the player can shoot left for the archery minigame.
|
* The amount of targets that the player can shoot left for the archery minigame.
|
||||||
*/
|
*/
|
||||||
private int archeryTargets = 0;
|
private int archeryTargets = 0;
|
||||||
|
|
||||||
private int archeryTotal = 0;
|
private int archeryTotal = 0;
|
||||||
|
|
||||||
public byte[] opCounts = new byte[255];
|
/**
|
||||||
|
* The save file version.
|
||||||
|
*/
|
||||||
|
public int version = ServerConstants.CURRENT_SAVEFILE_VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet administration.
|
||||||
|
* opCounts is used to enforce an authentic limit of 10 of each inbound packet per user per tick.
|
||||||
|
*/
|
||||||
|
public byte[] opCounts = new byte[255];
|
||||||
public int invalidPacketCount = 0;
|
public int invalidPacketCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -611,7 +619,7 @@ public class Player extends Entity {
|
||||||
if (this.isArtificial() && killer instanceof NPC) {
|
if (this.isArtificial() && killer instanceof NPC) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (killer instanceof Player && getWorldTicks() - killer.getAttribute("/save:last-murder-news", 0) >= 500) {
|
if (killer instanceof Player && killer.getName() != getName() /* happens if you died via typeless damage from an external cause, e.g. bugs in a dark cave without a light source */ && getWorldTicks() - killer.getAttribute("/save:last-murder-news", 0) >= 500) {
|
||||||
Item wep = getItemFromEquipment((Player) killer, EquipmentSlot.WEAPON);
|
Item wep = getItemFromEquipment((Player) killer, EquipmentSlot.WEAPON);
|
||||||
sendNews(killer.getUsername() + " has murdered " + getUsername() + " with " + (wep == null ? "their fists." : (StringUtils.isPlusN(wep.getName()) ? "an " : "a ") + wep.getName()));
|
sendNews(killer.getUsername() + " has murdered " + getUsername() + " with " + (wep == null ? "their fists." : (StringUtils.isPlusN(wep.getName()) ? "an " : "a ") + wep.getName()));
|
||||||
killer.setAttribute("/save:last-murder-news", getWorldTicks());
|
killer.setAttribute("/save:last-murder-news", getWorldTicks());
|
||||||
|
|
@ -630,7 +638,7 @@ public class Player extends Entity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GroundItemManager.create(new Item(526), getLocation(), k);
|
GroundItemManager.create(new Item(BONES_526), this.getAttribute("/save:original-loc",location), k);
|
||||||
final Container[] c = DeathTask.getContainers(this);
|
final Container[] c = DeathTask.getContainers(this);
|
||||||
|
|
||||||
for (Item i : getEquipment().toArray()) {
|
for (Item i : getEquipment().toArray()) {
|
||||||
|
|
@ -684,6 +692,10 @@ public class Player extends Entity {
|
||||||
skullManager.setSkulled(false);
|
skullManager.setSkulled(false);
|
||||||
removeAttribute("combat-time");
|
removeAttribute("combat-time");
|
||||||
getPrayer().reset();
|
getPrayer().reset();
|
||||||
|
removeAttribute("original-loc"); //in case you died inside a random event
|
||||||
|
interfaceManager.openDefaultTabs(); //in case you died inside a random that had blanked them
|
||||||
|
setComponentVisibility(this, 548, 69, false); //reenable the logout button (SD)
|
||||||
|
setComponentVisibility(this, 746, 12, false); //reenable the logout button (HD)
|
||||||
super.finalizeDeath(killer);
|
super.finalizeDeath(killer);
|
||||||
appearance.sync();
|
appearance.sync();
|
||||||
if (!getSavedData().getGlobalData().isDeathScreenDisabled()) {
|
if (!getSavedData().getGlobalData().isDeathScreenDisabled()) {
|
||||||
|
|
|
||||||
|
|
@ -152,18 +152,8 @@ public final class LoginConfiguration {
|
||||||
if (item == null) continue;
|
if (item == null) continue;
|
||||||
player.getEquipment().remove(item);
|
player.getEquipment().remove(item);
|
||||||
if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) {
|
if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) {
|
||||||
if (player.getInventory().add(item))
|
player.sendMessage(colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped."));
|
||||||
player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped."));
|
addItemOrBank(player, item.getId(), item.getAmount());
|
||||||
else if (player.getBankPrimary().add(item))
|
|
||||||
player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", it has been sent to your bank."));
|
|
||||||
else if (player.getBankSecondary().add(item))
|
|
||||||
player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", it has been sent to your secondary bank."));
|
|
||||||
else {
|
|
||||||
player.sendMessage (colorize("%RAs you can no longer wear " + item.getName() + ", and your inventory and both banks are full,"));
|
|
||||||
player.sendMessage (colorize("%RIt has been placed on the ground under your feet. Don't forget to grab it."));
|
|
||||||
player.sendMessage ("(Also, consider cleaning out your banks maybe? I mean jesus.)");
|
|
||||||
GroundItemManager.create (item, player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,6 @@ class PlayerSaveParser(val player: Player) {
|
||||||
reader ?: log(this::class.java, Log.WARN, "Couldn't find save file for ${player.name}, or save is corrupted.").also { read = false }
|
reader ?: log(this::class.java, Log.WARN, "Couldn't find save file for ${player.name}, or save is corrupted.").also { read = false }
|
||||||
if (read) {
|
if (read) {
|
||||||
saveFile = parser.parse(reader) as JSONObject
|
saveFile = parser.parse(reader) as JSONObject
|
||||||
}
|
|
||||||
|
|
||||||
if (read) {
|
|
||||||
parseData()
|
parseData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +77,7 @@ class PlayerSaveParser(val player: Player) {
|
||||||
parseStatistics()
|
parseStatistics()
|
||||||
parseAchievements()
|
parseAchievements()
|
||||||
parsePouches()
|
parsePouches()
|
||||||
parsePouches()
|
parseVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runContentHooks()
|
fun runContentHooks()
|
||||||
|
|
@ -377,5 +374,11 @@ class PlayerSaveParser(val player: Player) {
|
||||||
player.settings.parse(settingsData)
|
player.settings.parse(settingsData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parseVersion() {
|
||||||
|
saveFile ?: return
|
||||||
|
player.version = 0
|
||||||
|
if (saveFile!!.containsKey("version")) {
|
||||||
|
player.version = saveFile!!["version"].toString().toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ class PlayerSaver (val player: Player){
|
||||||
saveStatManager(saveFile)
|
saveStatManager(saveFile)
|
||||||
saveAttributes(saveFile)
|
saveAttributes(saveFile)
|
||||||
savePouches(saveFile)
|
savePouches(saveFile)
|
||||||
|
saveVersion(saveFile)
|
||||||
contentHooks.forEach { it.savePlayer(player, saveFile) }
|
contentHooks.forEach { it.savePlayer(player, saveFile) }
|
||||||
return saveFile
|
return saveFile
|
||||||
}
|
}
|
||||||
|
|
@ -96,6 +97,10 @@ class PlayerSaver (val player: Player){
|
||||||
player.pouchManager.save(root)
|
player.pouchManager.save(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveVersion(root: JSONObject){
|
||||||
|
root.put("version", player.version)
|
||||||
|
}
|
||||||
|
|
||||||
fun saveAttributes(root: JSONObject){
|
fun saveAttributes(root: JSONObject){
|
||||||
if(player.gameAttributes.savedAttributes.isNotEmpty()){
|
if(player.gameAttributes.savedAttributes.isNotEmpty()){
|
||||||
val attrs = JSONArray()
|
val attrs = JSONArray()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package core.game.node.entity.player.info.login
|
||||||
|
|
||||||
|
import core.ServerConstants
|
||||||
|
import core.api.*
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.item.Item
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs one-time save-version-related hooks.
|
||||||
|
* @author Player Name
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SaveVersionHooks : LoginListener {
|
||||||
|
|
||||||
|
override fun login(player: Player) {
|
||||||
|
if (player.version < ServerConstants.CURRENT_SAVEFILE_VERSION) {
|
||||||
|
sendMessage(player, "<col=CC6600>Migrating save file version ${player.version} to current save file version ${ServerConstants.CURRENT_SAVEFILE_VERSION}.</col>")
|
||||||
|
|
||||||
|
// Perform actual migrations
|
||||||
|
if (player.version < 1) { // GL #1811
|
||||||
|
// Give out crafting hoods if the player bought any crafting capes when the hoods were not obtainable
|
||||||
|
var hasHoods = 0
|
||||||
|
var hasCapes = 0
|
||||||
|
val searchSpace = arrayOf(player.inventory, player.bankPrimary, player.bankSecondary)
|
||||||
|
for (container in searchSpace) {
|
||||||
|
for (hood in container.getAll(Item(Items.CRAFTING_HOOD_9782))) {
|
||||||
|
hasHoods += hood.amount
|
||||||
|
}
|
||||||
|
for (id in arrayOf(Items.CRAFTING_CAPE_9780, Items.CRAFTING_CAPET_9781)) {
|
||||||
|
for (cape in container.getAll(Item(id))) {
|
||||||
|
hasCapes += cape.amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val need = hasCapes - hasHoods
|
||||||
|
if (need > 0) {
|
||||||
|
sendMessage(player, "<col=CC6600>You are being given $need crafting hood(s), because we think you bought $need crafting cape(s) when the hoods were still unobtainable.</col>")
|
||||||
|
addItemOrBank(player, Items.CRAFTING_HOOD_9782, need)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock Surok's Theme if eligible
|
||||||
|
if (getQuestStage(player, "What Lies Below") > 70) {
|
||||||
|
player.musicPlayer.unlock(250, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate random-event saved location attributes to the uniform naming scheme
|
||||||
|
for (old in arrayOf("/save:drilldemon:original-loc","/save:evilbob:prevlocation","/save:freakyf:location","supexam:loc")) {
|
||||||
|
val oldloc = player.getAttribute(old, player.location)
|
||||||
|
if (oldloc != player.location) {
|
||||||
|
player.setAttribute("/save:original-loc", oldloc)
|
||||||
|
}
|
||||||
|
player.removeAttribute(old)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the missing tutorial island varp if eligible
|
||||||
|
if (getAttribute(player, "/save:tutorial:complete", false)) {
|
||||||
|
setVarp(player, 281, 1000, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish up
|
||||||
|
player.version = ServerConstants.CURRENT_SAVEFILE_VERSION
|
||||||
|
sendMessage(player, "<col=CC6600>Save file migration complete. Happy scaping!</col>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue