I was wondering if someone could help me fix a small issue. This mud server or telnet server if you will doesn't keep the connections alive after a while.
I was wondering if someone could help fix either a memory leak or some other problem I'm overlooking.
I'm not good with memory access stuff yet but the program seems to hold on for around 13 hours since I got it to send an "'" string but when the last connection is lost it crashes the server completely and I think it has something to do with the list but even running locally there's a problem this is why I figured it was a memory issue.
Here is the complete code.
Code: Select all
EnableExplicit
Structure Person
Name.s
rank.i
Gender.i
Password.s
PassTries.i
Email.s
health.i
strength.i
vitality.i
moves.i
CNum.i
LastData.i
ElTime.i
PFlag.b
Text.s
command.s
LastTell.s
KeepAlive.b
QFlag.b
Logged.b
EndStructure
Global NewList Players.Person()
Global MudName.s
Global MaxPlayers.i
Global port.i
Global Exit.b
Procedure MsgAll(name.s, msg.s, logged.b = #True)
ForEach Players()
If logged
If name <> Players()\Name And Players()\Logged
SendNetworkString(Players()\CNum, msg + #CR$)
EndIf
Else
If name <> Players()\Name
SendNetworkString(Players()\CNum, msg + #CR$)
EndIf
EndIf
Next
EndProcedure
Procedure Timer(*Person.Person)
*Person\ElTime = AddDate(Date(), #PB_Date_Minute, 1)
EndProcedure
Procedure Test(*Person.Person)
SendNetworkString(*Person\CNum, "Current directory is: " + GetCurrentDirectory())
EndProcedure
Procedure Time(*Person.Person)
Protected dom.s = " " +Str(Day(Date())) + ", "
Protected mth.s
If Month(Date()) = 1
mth = " January "
ElseIf Month(Date()) = 2
mth = " February "
ElseIf Month(Date()) = 3
mth = " March "
ElseIf Month(Date()) = 4
mth = " April "
ElseIf Month(Date()) = 5
mth = " May "
ElseIf Month(Date()) = 6
mth = " June "
ElseIf Month(Date()) = 7
mth = " July "
ElseIf Month(Date()) = 8
mth = " August "
ElseIf Month(Date()) = 9
mth = " September "
ElseIf Month(Date()) = 10
mth = " October "
ElseIf Month(Date()) = 11
mth = " November "
Else
mth = " December "
EndIf
Protected dw.s
If DayOfWeek(Date()) = 0
dw = " Sunday, "
ElseIf DayOfWeek(Date()) = 1
dw = " Monday, "
ElseIf DayOfWeek(Date()) = 2
dw = " Tuesday, "
ElseIf DayOfWeek(Date()) = 3
dw = " Wednesday, "
ElseIf DayOfWeek(Date()) = 4
dw = " Thursday, "
ElseIf DayOfWeek(Date()) = 5
dw = " Friday, "
Else
dw = " Saturday, "
EndIf
Protected hr.s = Str(Hour(Date()))
If Hour(Date()) = 0
hr = "12"
EndIf
Protected mn.s = Str(Minute(Date()))
Protected dayhour.s
If Hour(Date()) >= 12
dayhour = "PM."
If Hour(Date()) > 12
hr = Str(Hour(Date()) -12)
EndIf
Else
dayhour = "aAM."
EndIf
SendNetworkString(*Person\CNum, "The current server time is: " + dw + mth + dom + Str(Year(Date())) + #CR$ + hr + ":" + mn + " " + dayhour + #CR$)
EndProcedure
Procedure Tell(*Person.Person)
Protected x.i = 0
Protected kick.s = ""
Protected who.s = ""
For x = 1 To CountString(*Person\Text, " ") + 1
If x = 2
who = StringField(*Person\Text, x, " ")
ElseIf x > 2
kick = kick + StringField(*Person\Text, x, " ") + " "
EndIf
Next
If who = ""
SendNetworkString(*Person\CNum, "You must type <tell> <playername> <message>." + #CR$)
ProcedureReturn
EndIf
If who = *Person\Name
SendNetworkString(*Person\CNum, "You can't tell yourself." + #CR$)
ProcedureReturn
EndIf
ForEach Players()
If Players()\Name = who
SendNetworkString(*Person\CNum, "You tell " + Players()\Name + ", " + kick + #CR$)
SendNetworkString(Players()\CNum, *Person\Name+ " tells you, " + kick + #CR$)
Players()\LastTell = *Person\Name
ProcedureReturn
EndIf
Next
SendNetworkString(*Person\CNum, "This player does not exist." + #CR$)
EndProcedure
Procedure Reply(*Person.Person)
Protected x.i = 0
Protected kick.s = ""
For x = 2 To CountString(*Person\Text, " ") + 1
kick = kick + StringField(*Person\Text, x, " ") + " "
Next
If kick = ""
SendNetworkString(*Person\CNum, "You must type <reply> <message>." + #CR$)
ProcedureReturn
EndIf
ForEach Players()
If Players()\Name = *Person\LastTell
SendNetworkString(*Person\CNum, "You tell " + Players()\Name + ", " + kick + #CR$)
SendNetworkString(Players()\CNum, *Person\Name+ " tells you, " + kick + #CR$)
Players()\LastTell = *Person\Name
ProcedureReturn
EndIf
Next
SendNetworkString(*Person\CNum, "This player does not exist." + #CR$)
EndProcedure
Procedure motd(*Person.Person)
Protected f.i
Protected RString.s
OpenFile(0, "motd.txt")
f = ReadFile(#PB_Any, "motd.txt")
SendNetworkString(*Person\CNum, "MOTD:" + #CR$)
While Eof(f) = 0
RString = ReadString(f)
SendNetworkString(*Person\CNum, RString + #CR$)
Wend
CloseFile(0)
EndProcedure
Procedure Register(*Person.Person)
ForEach Players()
If Players() <> *Person And *Person\Name = Players()\Name
Players()\QFlag = #True
Break
EndIf
Next
Protected RString.s
Protected f.i
If *Person\Logged = #True
MsgAll(*Person\Name, *Person\Name + " has entered the game.")
SendNetworkString(*Person\CNum, "Welcome To " + MudName + "! Type help For the List of commands. The current message of the day is:" + #CR$)
PrintN(*Person\Name + " has just logged in.")
OpenFile(0, "motd.txt")
f = ReadFile(#PB_Any, "motd.txt")
While Eof(f) = 0
RString = ReadString(f)
SendNetworkString(*Person\CNum, RString + #CR$)
Wend
CloseFile(0)
EndIf
*Person\PFlag = #True
Timer(*Person)
*Person\command = "game"
Time(*Person)
EndProcedure
Procedure.s StrGender(*Person.Person)
If *Person\Gender = 1
ProcedureReturn "male"
ElseIf *Person\Gender = 2
ProcedureReturn "female"
EndIf
EndProcedure
Procedure Gender(*Person.Person)
If *Person\Text = ""
SendNetworkString(*Person\CNum, "Please choose your gender." + #CR$ + "1 for male or 2 for female." + #CR$)
ProcedureReturn
EndIf
If Val(*Person\Text) > 0 And Val(*Person\Text) < 3
*Person\Gender = Val(*Person\Text)
SendNetworkString(*Person\CNum, "You are now a " + StrGender(*Person) + "." + #CR$)
*Person\rank = 1
*Person\health = 10000
*Person\strength = 3
*Person\vitality = 3
*Person\moves = 3
*Person\Logged = #True
Register(*Person)
Else
SendNetworkString(*Person\CNum, "You must choose either 1 for male or 2 for female." + #CR$)
EndIf
EndProcedure
Procedure Name(*Person.Person)
If *Person\Text = ""
SendNetworkString(*Person\CNum, "Please type your chosen name.")
ProcedureReturn
EndIf
Protected f.i
Protected RString.s
If FileSize("players\"+*Person\Text+".plr") > -1
f = ReadFile(#PB_Any, "players/" + *Person\Text+".plr")
RString = ReadString(f)
*Person\Name = RString
RString = ReadString(f)
*Person\rank = Val(RString)
RString = ReadString(f)
*Person\Password = RString
RString = ReadString(f)
*Person\Gender = Val(RString)
RString = ReadString(f)
*Person\health = Val(RString)
RString = ReadString(f)
*Person\strength = Val(RString)
RString = ReadString(f)
*Person\vitality = Val(RString)
RString = ReadString(f)
*Person\moves = Val(RString)
SendNetworkString(*Person\CNum, "The character " + *Person\Text + " already exists." + #CR$)
CloseFile(f)
Else
*Person\Name = *Person\Text
SendNetworkString(*Person\CNum, "Welcome, " + *Person\Name + "." + #CR$)
EndIf
*Person\command = "password"
If *Person\Password <> ""
SendNetworkString(*Person\CNum, "Please type the existing password For " + *Person\Name + ".")
Else
SendNetworkString(*Person\CNum, "Please type the new password For " + *Person\Name + ".")
EndIf
EndProcedure
Procedure Boot(*Person.Person)
Protected x.i = 0
Protected kick.s = ""
Protected who.s = ""
For x = 1 To CountString(*Person\Text, " ") + 1
If x = 2
who = StringField(*Person\Text, x, " ")
ElseIf x > 2
kick = kick + StringField(*Person\Text, x, " ") + " "
EndIf
Next
If who = ""
SendNetworkString(*Person\CNum, "You must type <boot> <playername> <reason> optional." + #CR$)
ProcedureReturn
EndIf
ForEach Players()
If Players()\Name = who And *Person\rank >= Players()\rank
SendNetworkString(*Person\CNum, Players()\Name + " has been kicked." + #CR$)
SendNetworkString(Players()\CNum, "You have been kicked by " + *Person\Name + " for the following reason: " + kick + #CR$)
Players()\QFlag = #True
ProcedureReturn
ElseIf *Person\rank < Players()\rank
SendNetworkString(*Person\CNum, "You do not have the authority to boot " + who + " from the server." + #CR$)
ProcedureReturn
EndIf
Next
SendNetworkString(*Person\CNum, "This player does not exist." + #CR$)
EndProcedure
Procedure Password(*Person.Person)
If *Person\command = "password" And *Person\Password <> ""
If *Person\Text = ""
SendNetworkString(*Person\CNum, "Please type the existing password For " + *Person\Name + ".")
ProcedureReturn
EndIf
If *Person\Text <> *Person\Password
*Person\PassTries = *Person\PassTries +1
If *Person\PassTries = 3
SendNetworkString(*Person\CNum, "You have made too many attempts to connect to this account and will be kicked from the server." + #CR$)
*Person\QFlag = #True
ProcedureReturn
EndIf
SendNetworkString(*Person\CNum, "The password you entered is incorrect." + #CR$ + "Please retype the password.")
Else
*Person\Logged = #True
Register(*Person)
EndIf
ElseIf *Person\command = "password" And *Person\Password = ""
If *Person\Text = ""
SendNetworkString(*Person\CNum, "Please type the new password For " + *Person\Name + ".")
ProcedureReturn
EndIf
*Person\Password = *Person\Text
*person\command = "repassword"
SendNetworkString(*Person\CNum, "Now please retype your password.")
ElseIf *Person\command = "repassword" And *Person\Password <> ""
If *Person\Text = ""
SendNetworkString(*Person\CNum, "Now please retype your password.")
ProcedureReturn
EndIf
If *Person\Text <> *Person\Password
SendNetworkString(*Person\CNum, "Passwords do not match." + #CR$ + "Please retype your password.")
*Person\Password = ""
*Person\command = "password"
Else
*person\command = "gender"
SendNetworkString(*Person\CNum, "Please choose your gender." + #CR$ + "1 for male or 2 for female." + #CR$)
EndIf
EndIf
EndProcedure
Procedure SaveFile(*Person.Person)
Protected f.i
CreateFile(f, "players/" + *Person\Name+".plr")
WriteStringN(f, *Person\Name + #CR$ + *Person\rank + #CR$ + *Person\Password + #CR$ + Str(*Person\Gender) + #CR$ + Str(*Person\health) + #CR$ + Str(*Person\strength) + #CR$ + Str(*Person\vitality) + #CR$ + Str(*Person\moves))
CloseFile(f)
EndProcedure
Procedure RFile(*Person.person)
Protected RString.s
Protected f.i
If FileSize("players\" + *Person\Name + ".plr") > -1
f = ReadFile(#PB_Any, "players/" + *Person\Name+".plr")
SendNetworkString(*Person\CNum, "Here is the file in its entirety." + #CR$)
While Eof(f) = 0
RString = ReadString(f)
SendNetworkString(*Person\CNum, RString + #CR$)
Wend
CloseFile(f)
Else
SendNetworkString(*Person\CNum, "Your data file does not exist yet. Please type save.")
EndIf
EndProcedure
Procedure Who(*Person.Person)
Protected c = 0
Protected people.s = ""
ForEach Players()
If Players()\Logged= #True
c = c + 1
EndIf
Next
SendNetworkString(*Person\CNum, "There are " + c + " players who are online:" + #CR$)
ForEach Players()
If Players()\Logged = #True
people = people + Players()\Name + #CR$
EndIf
Next
SendNetworkString(*Person\CNum, people + #CR$)
*Person\Text = ""
EndProcedure
Procedure Quit(*Person.Person)
SendNetworkString(*Person\CNum, "Goodbye " + *Person\Name + ". Thanks For checking out " + MudName + "." + #CR$)
*Person\QFlag = #True
Delay(10)
EndProcedure
Procedure help(*Person.Person)
SendNetworkString(*Person\CNum, "The game is still being developed so test functions are a must." + #CR$)
SendNetworkString(*Person\CNum, "Type info to get information about the game,"+ #CR$)
SendNetworkString(*Person\CNum, "stats to see info about you," + #CR$)
SendNetworkString(*Person\CNum, "who to find out how many people are online," + #CR$)
SendNetworkString(*Person\CNum, "shutdown To completely shut down the server,"+ #CR$)
SendNetworkString(*Person\CNum, "quit to close the connection and delete your player info,"+ #CR$)
SendNetworkString(*Person\CNum, "or help for this help information."+ #CR$)
EndProcedure
Procedure say(*Person.Person)
Protected SayMsg.s
SendNetworkString(*Person\CNum, "You say: " + Right(*Person\Text, Len(*Person\Text) -4) + #CR$)
SayMsg = Right(*Person\Text, Len(*Person\Text) -4)
ForEach Players()
If Players()\Name = *Person\Name Or Players()\Logged = #False
Continue
EndIf
SendNetworkString(Players()\CNum, *Person\Name + " says: " + SayMsg + #CR$)
Next
EndProcedure
Procedure shutdown(*Person.Person)
Protected SayMsg.s
SayMsg = Right(*Person\Text, Len(*Person\Text) -9)
ForEach Players()
SendNetworkString(Players()\CNum, "Server shutdown initiated by " + *Person\Name + " for the following reason: " + SayMsg + #CR$)
Quit(Players())
Next
EndProcedure
Procedure info(*Person.Person)
SendNetworkString(*Person\CNum, "Dark ages written by Justin Miller." + #CR$)
SendNetworkString(*Person\CNum, "copyright 2018 by Justin Miller." + #CR$)
*Person\Text = ""
EndProcedure
Procedure NetworkConnectEvent(cnt.i)
Protected *Person.Person
If MaxPlayers = ListSize(Players())
SendNetworkString(cnt, "Max clients are reached!")
Print("Max clients are reached!")
Else
ForEach Players()
If Players()\CNum = cnt
*Person = Players()
EndIf
Next
If *Person = #Null
*Person = AddElement(Players())
*Person\CNum = cnt
*Person\LastData = Date()
*Person\command = "registering"
EndIf
Delay(500)
SendNetworkString(cnt, "Welcome to " + MudName + ". " + #CR$ + "This game is under development." + #CR$)
PrintN("A client has connected with the ip " + IPString(GetClientIP(cnt)) + ".")
If *person\Name = ""
Name(*Person)
EndIf
EndIf
EndProcedure
Procedure NetworkDataEvent(cnt.i)
Protected.i received
Protected *data
Protected *Person.Person
ForEach Players()
If Players()\CNum = cnt
*Person = Players()
EndIf
Next
If *Person = #Null
*Person = AddElement(Players())
*Person\CNum = cnt
EndIf
*Person\LastData = Date()
*data = AllocateMemory(4096, #PB_Memory_NoClear)
If *data
received = ReceiveNetworkData(cnt, *data, MemorySize(*Data))
If received > 0
*Person\Text = PeekS(*data, received, #PB_UTF8|#PB_ByteLength)
*Person\Text = Left(*Person\Text, Len(*Person\Text) - 2)
If *Person\command = "game"
Protected cmd.s = StringField(*Person\Text, 1, " ")
*Person\Text = ReplaceString(*Person\Text, cmd, LCase(cmd))
If Left(*Person\Text, 3) = "say"
say(*Person)
ElseIf *Person\Text = "test"
Test(*Person)
ElseIf *Person\Text = "prompt"
If *Person\PFlag = #False
*Person\PFlag = #True
SendNetworkString(*Person\CNum, "Your prompt has been turned on." + #CR$)
ElseIf *Person\PFlag
*Person\PFlag = #False
SendNetworkString(*Person\CNum, "Your prompt has been turned off." + #CR$)
EndIf
ElseIf Left(*Person\Text, 4) = "time"
Time(*Person)
ElseIf Left(*Person\Text, 8) = "announce"
MsgAll(*Person\Name, *Person\Name + " has made an announcement." + #CR$ + Right(*Person\Text, Len(*Person\Text) -9), #True)
SendNetworkString(*Person\CNum, "Your announcement was sent.")
ElseIf Left(*Person\Text, 4) = "tell"
Tell(*Person)
ElseIf Left(*Person\Text, 5) = "reply"
Reply(*Person)
ElseIf Left(*Person\Text, 4) = "boot"
If *Person\rank < 3
SendNetworkString(*Person\CNum, "You do not have permission to use this command.")
ProcedureReturn
EndIf
Boot(*Person)
ElseIf *Person\Text = "motd"
motd(*Person)
ElseIf *Person\Text = "save"
SaveFile(*Person)
SendNetworkString(*Person\CNum, "Your player info has been saved.")
ElseIf *Person\Text = "keepalive"
If *Person\KeepAlive = 0
*Person\KeepAlive = 1
SendNetworkString(*Person\CNum, "Your keep alive flag is now on and you will not be disconnected from the server." + #CR$)
ElseIf *Person\KeepAlive = 1
*Person\KeepAlive = 0
SendNetworkString(*Person\CNum, "Your keep alive flag has been turned off. After 3 minutes of inactivity you will be disconnected." + #CR$)
*Person\LastData = Date()
EndIf
ElseIf *Person\Text = "rf"
RFile(*Person)
ElseIf *Person\Text = "stats"
SendNetworkString(*Person\CNum, "You are " + *Person\Name + " rank " + *Person\rank + #CR$)
SendNetworkString(*Person\CNum, " You are " + StrGender(*Person) + #CR$)
SendNetworkString(*Person\CNum, " You have " + *Person\health + " health, " + #CR$ + *Person\strength + " strength, " + *Person\vitality + " vitality, and " + *Person\moves + " moves." + #CR$)
ElseIf *Person\Text = "who"
Who(*Person)
ElseIf Left(*Person\Text, 8) = "shutdown"
If *Person\rank < 5
SendNetworkString(*Person\CNum, "You may not use this command because you are not the server administrator.")
ProcedureReturn
EndIf
*Person\command = "shutdown"
shutdown(*Person)
Exit = #True
ElseIf *Person\Text = "info"
info(*Person)
ElseIf *Person\Text = "quit"
Quit(*Person)
ElseIf *Person\Text = "help"
help(*Person)
Else
SendNetworkString(*Person\CNum, "Huh?" + #CR$)
EndIf
ElseIf *Person\command = "registering"
Name(*Person)
ElseIf *Person\command = "password"
Password(*Person)
ElseIf *Person\command = "repassword"
Password(*Person)
ElseIf *Person\command = "gender"
Gender(*Person)
EndIf
If Players()\Logged And Players()\PFlag
SendNetworkString(*Person\CNum, Str(*Person\health) + "health, " + Str(*Person\strength) + "strength, " + Str(*Person\vitality) + "vitality, " + Str(*Person\moves) + "moves")
EndIf
*Person\Text = ""
FreeMemory(*data)
EndIf
EndIf
EndProcedure
Procedure Main()
OpenConsole("Server setup")
Delay(100)
OpenFile(0, "motd.txt")
CloseFile(0)
If FileSize("settings.conf") = -1
CreateFile(0, "settings.conf")
Print ("What's the maximum number of players allowed to connect to the server?")
MaxPlayers = Val(Input())
If MaxPlayers < 1
PrintN("Default: The Default maximum players allowed is 3.")
MaxPlayers = 3
EndIf
Print ("Please enter a name for your game.")
MudName = Input()
ConsoleTitle(MudName)
Print ("Please enter a port number for incomming connections.")
port = Val(Input())
If port = 0
PrintN("Port must have a value. Since nothing was entered the port will be set to 4000 as the default.")
port = 4000
EndIf
WriteStringN(0, Str(MaxPlayers))
WriteStringN(0, MudName)
WriteStringN(0, Str(port))
CloseFile(0)
PrintN("The server configuration settings have been saved to settings.conf.")
Else
OpenFile(0, "settings.conf")
MaxPlayers = Val(ReadString(0))
PrintN("Max players is set to: " + Str(MaxPlayers))
MudName = ReadString(0)
ConsoleTitle(MudName)
PrintN("Mud name is set to: " + MudName)
port = Val(ReadString(0))
PrintN("Port is set to: " + Str(port))
CloseFile(0)
EndIf
If CreateNetworkServer(0, port, #PB_Network_TCP, "0.0.0.0") = 0
PrintN("Error: The server could Not be started.")
End
EndIf
PrintN("Server started successfully.")
Repeat
Inkey()
If RawKey() = 27
PrintN("Exit: The server will now close.")
Exit = #True
EndIf
Select NetworkServerEvent(0)
Case #PB_NetworkEvent_Connect
NetworkConnectEvent(EventClient())
Case #PB_NetworkEvent_Data
NetworkDataEvent(EventClient())
Case #PB_NetworkEvent_None
Protected x.i = ListSize(Players())
Delay(5)
For x = ListSize(Players()) -1 To 0 Step -1
SelectElement(Players(), x)
If Players()\KeepAlive
Players()\LastData = Date()
EndIf
If Date() = Players()\ElTime
SendNetworkString(Players()\CNum, "'")
Timer(Players())
EndIf
If (Players()\QFlag = #True) Or (Players()\LastData < Date() - 180 And Players()\KeepAlive = #False)
If Players()\QFlag
MsgAll(Players()\Name, Players()\Name + "has left the game.")
SelectElement(Players(), x)
PrintN("Info: " + Players()\Name + " has chosen to disconnect.")
Else
If Players()\Logged = #False
PrintN("Info: Client " + Str(Players()\CNum) + " disconnected due to inactivity.")
Else
PrintN(Players()\Name + " has been disconnected do to inactivity.")
SendNetworkString(Players()\CNum, "You have been disconnected because you were idle for 3 minutes.")
EndIf
EndIf
CloseNetworkConnection(Players()\CNum)
DeleteElement(Players())
If ListSize(Players()) = 0
ClearList(Players())
EndIf
EndIf
Next
EndSelect
Until Exit
CloseNetworkServer(0)
EndProcedure
Main()// Code tags added (Kiffi)



