ID:2794752
 
(See the best response by F0lak.)
Code:
client
Del()
for(var/obj/Seclusion_Cushion/C in view())
if(usr.trainingprep)
usr.starttime = worldage
usr.offlinetraining = 1
usr.KarmaSave()
usr.Save()
del(src)


mob
proc
PermaDeath()
src << "<b><font color=#FF0000>You feel your eyelids shut heavily for the last time...</b></font>"
if(fdel("./save/[ckey]"))
src << "save deleted"
else
src << "try again"
winset(src,null,"command=.reconnect")


Problem description:
I used to have the first bit under mob/Logout() but it didn't work properly, and started working as intended when I switched to client/Del. However, after doing so the PermaDeath proc stopped working properly. When it runs, the player ends up in an infinite loop of seeing the death message and just reconnecting with their savefile intact.

I think the issue is that after deleting the savefile, the Del proc immediately makes a new one when the player is disconnected, but I have no clue how to make it so Del only functions when the player logs out normally rather than being disconnected by the PermaDeath proc. Does anyone know how I could accomplish this?
So I called attention to this in the BYONDiscord and I figure the simplest solution for you would just to make a client variable (call it something simple like noSave) and set it to 1 or true when performing PermaDeath(). Then, in your client's Del() override, just check if that variable is true and if so, don't save.

However we were curious why you decided to go the route of forcing a reconnect instead of just wiping the save and jettisoning the player to the title screen or starting page (or however you handle initial connection). If possible, consider going that route as it would probably feel more fluid to the player to be naturally brought to where they need to be instead of utilizing reconnect trickery, and as a bonus you wouldn't have to add any additional logic to your client's Del() override.

Thematically, you could also cause a fade-to-black during permadeath to add to the "Yeah, you're character's gone forever" feeling, though that's probably beyond the scope of what you were looking for with this thread.
var
premiumrooms = 3
standardrooms = 4

mob
proc
OfflineCultivation()
var/length
if(worldage < offlinelimit) length = worldage - starttime
else length = offlinelimit - starttime
var/cycles = length * 3600
var/gain
var/gains
for(var/obj/Bodypart/Dantian/D in usr)
if(D.equipped)
while(cycles)
if(RealmNum == 1)
if(Skin < 100)
Skin += (1/18)
if(Skin > 100) Skin = 100
if(Skin == 100)
QuickBreakthrough(3)
MortalRealm = "Muscle Refining"
usr << "[Stage]"
else
if(Muscle < 100)
Muscle += (1/72)
if(Muscle > 100) Muscle = 100
if(Muscle == 100)
QuickBreakthrough(3)
MortalRealm = "Bone Refining"
usr << "[Stage]"
else
if(Bone < 100)
Bone += (1/144)
if(Bone > 100) Bone = 100
if(Bone == 100)
QuickBreakthrough(2)
MortalRealm = "Peak"
usr << "[Stage]"
if(RealmNum > 1 && D.qi < (D.maxqi * Tolerance))
gain = ((((AbsorbStrength + (AbsorbStrength - 1)) ** 2) / 10) * (QiDensity + SpiritStoneLevel))
gain = (gain / (10 ** (RealmNum - CultivationSkillLevel)))
if(!ElementCompatible)
if(D.qi >= D.highestqi)
if(gain > (AbsorbEfficiency / 10)) gain = AbsorbEfficiency / 10 // If cultivation techniques element does not match one of the user's elements, their base absorb efficiency determines maximum gains.
else
if(D.qi + AbsorbEfficiency <= D.highestqi)
if(gain > AbsorbEfficiency) gain = AbsorbEfficiency// If catching up to old cultivation, 10x greater gains.
if(D.qi + AbsorbEfficiency > D.highestqi)
if(gain > AbsorbEfficiency) gain = D.highestqi - D.qi
if(ElementCompatible)
if(D.qi >= D.highestqi)
if(gain > (AbsorbEfficiency / 5)) gain = AbsorbEfficiency / 5
else
if(D.qi + (AbsorbEfficiency * 2) < D.highestqi)
if(gain > AbsorbEfficiency * 2) gain = AbsorbEfficiency * 2
if(D.qi + (AbsorbEfficiency * 2) > D.highestqi)
gain = D.highestqi - D.qi
if(QinHeartQi)
if(prob(10))
gain *= rand(16,22)
gains = gain + rand(-0.2,0.2)
if(prob(0.8)) AbsorbEfficiency += (gains * (rand(8,12) / 100))
if(gains < 0) gains = 0
if(D.qi >= D.highestqi)
if(SpiritStoneQi > 0 && gains < (AbsorbEfficiency * 0.3))
if(SpiritStoneQi > (AbsorbEfficiency * 0.3))
SpiritStoneQi -= ((AbsorbEfficiency * 0.3) - gains) * (10 ** (usr.RealmNum-usr.SpiritStoneLevel))
gains = AbsorbEfficiency * 0.3
else
gains += SpiritStoneQi
SpiritStoneQi = 0
else
if(SpiritStoneQi > 0 && gains < (AbsorbEfficiency * 3))
if(SpiritStoneQi > (AbsorbEfficiency * 3))
SpiritStoneQi -= ((AbsorbEfficiency * 3) - gains) * (10 ** (usr.RealmNum-usr.SpiritStoneLevel))
gains = AbsorbEfficiency * 3
else
gains += SpiritStoneQi
SpiritStoneQi = 0
if(D.qi >= D.highestqi)
if(CultivationBoost > 0 && gains < (AbsorbEfficiency * 0.4))
if(CultivationBoost > (AbsorbEfficiency * 0.4))
CultivationBoost -= (AbsorbEfficiency * 0.4) - gains
gains = AbsorbEfficiency * 0.4
else
gains += CultivationBoost
CultivationBoost = 0
else
if(CultivationBoost > 0 && gains < (AbsorbEfficiency * 4))
if(CultivationBoost > (AbsorbEfficiency * 4))
CultivationBoost -= (AbsorbEfficiency * 4) - gains
gains = AbsorbEfficiency * 4
else
gains += CultivationBoost
CultivationBoost = 0
D.qi += gains
else
if((CultivationSkill == null && CultivationSkillLevel != 1) || (CultivationSkill && CultivationSkillLevel < CultivationSkillRealm))
if(CultivationSkillExp < CultivationSkillMExp)
if(enlightened)
if(prob(70))
CultivationSkillExp += rand(0.4,0.8)
else
if(prob(10))
CultivationSkillExp += rand(0.1,0.2)
if(CultivationSkillExp >= CultivationSkillMExp)
if(usr.Physique == "Epiphany" || usr.Physique == "Musing Devil")
usr << "You experience an epiphany."
CultivationSkillLevelCheck()
else
if(enlightened)
if(prob(5))
usr << "You experience an epiphany."
CultivationSkillLevelCheck()
else
if(prob(0.1))
usr << "You experience an epiphany."
CultivationSkillLevelCheck()
else
if((Technique == null && TechniqueLevel != 1) || (Technique && TechniqueLevel < TechniqueRealm))
if(TechniqueExp < TechniqueMExp)
if(enlightened)
if(prob(70))
TechniqueExp += rand(0.4,0.8)
else
if(prob(10))
TechniqueExp += rand(0.1,0.2)
if(TechniqueExp >= TechniqueMExp)
if(enlightened)
if(prob(5))
usr << "You experience an epiphany."
TechniqueLevelCheck()
else
if(prob(0.1))
usr << "You experience an epiphany."
TechniqueLevelCheck()
else
switch(ImbuementPractice)
if("Unarmed")
for(var/obj/Element/Unarmed/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Swordsmanship")
for(var/obj/Element/Swordsmanship/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Archery")
for(var/obj/Element/Archery/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Fire")
for(var/obj/Element/Fire/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Water")
for(var/obj/Element/Water/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Earth")
for(var/obj/Element/Earth/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Metal")
for(var/obj/Element/Metal/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Wood")
for(var/obj/Element/Wood/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Yang")
for(var/obj/Element/Yang/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Yin")
for(var/obj/Element/Yin/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Buddha")
for(var/obj/Element/Buddha/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
if("Demon")
for(var/obj/Element/Demon/F in usr)
if(F.Level == RealmNum * 3 && F.Exp + 1 == F.MExp) cycles = 0
F.ElementLevelCheck(usr,RealmNum * 3,1,40)
cycles -= 1
sleep(1)
for(var/obj/Key/K in usr)
if(offlinelimit > worldage)
usr << "<b>As you leave the seclusion chamber ahead of time, an assistant brings you gold to reimburse you for the unspent time.</b>"
if(findtext(K.Door,"Standard"))
usr.gold += (offlinelimit - worldage) * 100
else
usr.gold += (offlinelimit - worldage) * 300
if(findtext(K.Door,"Standard") || findtext(K.Door,"Premium"))
del(K)
offlinetraining = 0
trainingprep = 0
starttime = null
offlinelimit = null
usr.loc = locate(ptx,pty,ptz)

var
offlinetraining = 0 // Used to determine if player gets offline gains while logging out.
starttime // The current year when the player logs off.
offlinelimit // The enddate of their service.
trainingprep = 0 // Used to tell the game to see if the player is in a seclusion chamber when they log out.
ptx // Coordinates to teleport player when they log back in.
pty
ptz
skeypass // Password to set the key to; standard room
pkeypass // premium room
ImbuementPractice = null
combatskilllist = list()

Zhao_Lianhua
NPC = 1
ptx = 62
pty = 83
ptz = 1
skeypass = "LQCStandard"
pkeypass = "LQCPremium"
New()
var/Shirt = 'Shirt.dmi'
var/Robe = 'Robe.dmi'
Shirt += rgb(185,185,185)
overlays += Shirt
overlays += Robe
return ..()

verb
Talk()
set src in view(2)
var/choice
var/length
var/cost
if(premiumrooms == 0 && standardrooms == 0)
usr << "My apologies, there are currently no rooms available at our cultivation ground."
return
choice = input("Welcome to the Sleeping Imp cultivation ground. Are you interested in renting a cultivation room?") in list("Yes","No")
if(choice == "No") return
if(premiumrooms == 0) // Room availability is not actually implemented yet.
choice = input("We currently only have standard rooms available. Will that be fine?") in list("Yes","No")
if(choice == "Yes") choice = "Standard"
else if(standardrooms == 0)
choice = input("We currently only have premium rooms available. These are more costly, but have small qi gathering arrays that can improve your cultivation speed by 25%. Would you like to rent a room?") in list("Yes","No")
if(choice == "Yes") choice = "Premium"
else
choice = input("We have two types of room available. Standard and Premium; Premium rooms have a small qi gathering array that can accelerate your cultivation by about 25%.") in list("Standard","Premium","Cancel")
var/obj/Key/K = new
K.disposable = 0
switch(choice)
if("Standard")
length = input("How many years do you intend to stay? It costs 100 gold per year you intend to seclude yourself, and after leaving you will be reimbursed for any unused time.") as num
cost = 100
K.Door = "LQCStandard"
if("Premium")
length = input("How many years do you intend to stay? It costs 300 gold per year you intend to seclude yourself, and after leaving you will be reimbursed for any unused time.") as num
cost = 300
K.Door = "LQCPremium"
if(length <= 0)
del(K)
return
cost *= length
usr.gold -= cost
usr.offlinelimit = worldage + length
usr.trainingprep = 1
K.loc = usr
usr.ptx = ptx
usr.pty = pty
usr.ptz = ptz
usr.ImbuementPractice = input("What skill would you like to practice if you reach the limit in your cultivation?") in usr.combatskilllist


I managed to fix the permadeath-del-logout-save dilemma, but this whole bit is the root of the actual problem I'm facing.

What's intended; Buy access to a room, leave game inside room, when you come back a bunch of gains are tallied up and given to the player. The player is then removed from the room and has their key removed until next time.

What happens; Buy access to a room, leave game inside room, when you come back a bunch of gains are tallied up and the entire server lags out and I have to forcibly stop everything, I bring the server back up, the gains are given to the player. The player is then removed from the room and has their key removed until next time.

I actually had this working properly after messing with the permadeath-del-logout-save thing, no lag or anything, then I found out I broke permadeath working. I managed to fix that up, with no changes to the above code, and the lag came back;

client
Del()
for(var/obj/Seclusion_Cushion/C in view())
if(usr.trainingprep)
usr.starttime = worldage
usr.offlinetraining = 1
..()
mob
Logout()
if(!usr.dead)
usr.KarmaSave()
usr.Save()
world << "Something went right, me."
del(src)

mob
proc
PermaDeath()
src << "<b><font color=#FF0000>You feel your eyelids shut heavily for the last time...</b></font>"
src.dead = 1
fdel("./save/[ckey]")
winset(src,null,"command=.reconnect")


As for keeping the reconnect, using Login() doesn't work and I'm not really familiar with any other means of restarting someone's game. Login() directly loads your character or starts a new one if you have no savefile, there is no new/load/delete title screen type thing, if that makes a difference.
Best response
Your sleep is outside of your while() loop, so you're looping through every body part length * 3600 times every tick, which is causing your 'lag'. Before you go and just indent it a tab, though, that proc is going to stall whatever is calling it, for the length of that loop * however many body parts you have.

There are much better ways to handle this instead of running a massive loop like this ten times every second. For example, you could just compare the time spent away and use the magic of math to tally everything up then, rather than this loop mess, which funny enough sounds like what you're trying to do in the first place.

As an aside, you're deleting your keys and and all that jazz after the first bodypart has been "trained". I don't know if that's intentional or not.