Jump to content

Recommended Posts

  • Active+ Member

Hi, i made some changes on this system so that it now can show multiple accounts slots and add/delete accounts easily
I'm not an experienced programmer by any means, so bugs might happen and the code isn't the most efficient and clean, so if anyone has a suggestion let me know!

https://metin2.dev/topic/20529-multiple-login-saver-system

First i recommend installing his system first because i will only be listing the changes i made and this wont work without his system installed first
Also, i will only show the steps to have 2 slots account, but they can easily be replicated to have multiple accounts.
Implemented and Tested on TMP4 Server Files but most likely will work on any other server files.

 

Be extremely careful with tabulations, i would recommend using Visual Studio Code or something similar to avoid problems with it

 

Small video:
.gif

Icons used for save and delete account button (it doesn't have on hover and on click icons so you will have to add them yourself)
Download -> 

This is the hidden content, please
 or 
This is the hidden content, please
 

 

So lets start

First delete the root/uiSelectCredentials.py  and uiscript/accountlistwindow files that wont be needed anymore
root/introwindow.py (This is configured for my own interface so you will have to change positions according to yours)

Spoiler
	#Acc 0
	{
		"name": "Acc0",
		"type": "button",
		"x" : -160, "y" : -50,
		"vertical_align": "center",
		"horizontal_align" : "center",
		"text": "|cffc8aa80",
		"default_image" : BUTTON_CHITRA + "login_0.tga",
		"over_image" : BUTTON_CHITRA + "login_1.tga",
		"down_image" : BUTTON_CHITRA + "login_2.tga",
	},
	{
		"name" : "DelAccount0",
		"type" : "button",
		"x" : -210, "y" : -50,
		"vertical_align": "center",
		"horizontal_align" : "center",
		"text": "|cffc8aa80",
		"default_image" : BUTTON_CHITRA + "del_button.tga",
	},
	{
		"name" : "saveAccount0",
		"type" : "button",
		"x" : -230, "y" : -50,
		"vertical_align": "center",
		"horizontal_align" : "center",
		"text": "|cffc8aa80",
		"default_image" : BUTTON_CHITRA + "saveButton.tga",
	},
	#Acc 1
	{
		"name" : "Acc1",
		"type" : "button",
		"x" : -160,
		"y" : -20,
		"vertical_align" : "center",
		"horizontal_align" : "center",
		"text": "|cffc8aa80",
		"default_image" : BUTTON_CHITRA + "login_0.tga",
		"over_image" : BUTTON_CHITRA + "login_1.tga",
		"down_image" : BUTTON_CHITRA + "login_2.tga",
	},
	{
		"name" : "DelAccount1",
		"type" : "button",
		"x" : -210, "y" : -20,
		"vertical_align": "center",
		"horizontal_align" : "center",
		"text": "|cffc8aa80",
		"default_image" : BUTTON_CHITRA + "del_button.tga",
	},
	{
		"name" : "saveAccount1",
		"type" : "button",
		"x" : -230, "y" : -20,
		"vertical_align": "center",
		"horizontal_align" : "center",
		"text": "|cffc8aa80",
		"default_image" : BUTTON_CHITRA + "saveButton.tga",
	},

 

 

root/intrologin.py (Remember you need to have the system from North implemented or you wont be able to find the correct lines)

Spoiler

REMEMBER, THIS IS ON PYTHON SO BE CAREFUL WITH TABULATIONS

On def Open(self) Find:

self.Show()

 

Add before:

self.LoadAccounts()

 

Find:

self.saveLoginButton        = GetObject("SaveLoginButton")
self.loadCredentialButton    = GetObject("LoadCredentials")
self.loginCredentialButton  = GetObject("LoginCredentials")

 

And replace with:

self.loginAcc0 = self.GetChild("Acc0")
self.loginAcc1 = self.GetChild("Acc1")

self.saveAcc0 = self.GetChild("saveAccount0")
self.saveAcc1 = self.GetChild("saveAccount1")

self.delAcc0 = self.GetChild("DelAccount0")
self.delAcc1 = self.GetChild("DelAccount1")

 

Next Find:

self.saveLoginButton.SetEvent(ui.__mem_func__(self.__OnClickSaveLoginButton))

self.loadCredentialButton.SetEvent(ui.__mem_func__(self.__OnClickLoadInfoButton))
self.loginCredentialButton.SetEvent(ui.__mem_func__(self.__OnClickLoginAutoButton))

 

And replace with:

self.saveAcc0.SetEvent(ui.__mem_func__(self.selectAcc0IndexSave))
self.saveAcc1.SetEvent(ui.__mem_func__(self.selectAcc1IndexSave))

self.delAcc0.SetEvent(ui.__mem_func__(self.selectAcc0IndexDel))
self.delAcc1.SetEvent(ui.__mem_func__(self.selectAcc1IndexDel))

self.loginAcc0.SetEvent(ui.__mem_func__(self.selectAcc0IndexLogin))
self.loginAcc1.SetEvent(ui.__mem_func__(self.selectAcc1IndexLogin))

 

Find def __OnClickLoginButton(self) and add before

	def LoadAccounts(self):
		file_objectRead = open("user//credentials", "r")
		credentials = file_objectRead.readlines()

		for i in credentials:
			ind = i.split(":")
			if ind[0] == '0':
				self.loginAcc0.SetText(ind[1])
			elif ind[0] == '1':
				self.loginAcc1.SetText(ind[1])
			elif ind[0] == '2':
				self.loginAcc2.SetText(ind[1])
			elif ind[0] == '3':
				self.loginAcc3.SetText(ind[1])

		file_objectRead.close()

 

Find def __OnClickLoginAutoButton and def __OnClickLoadInfoButton and def __OnClickSaveLoginButton and delete this functions, they are no longer needed (this step is optional and the it will work normally if u followed the steps correctly)

Find def encode(self, key, clear) and add before:

	def selectAcc0IndexSave(self):
		self.saveAccount(0)

	def selectAcc1IndexSave(self):
		self.saveAccount(1)

	def selectAcc0IndexDel(self):
		self.delAccount(0)

	def selectAcc1IndexDel(self):
		self.delAccount(1)

	def selectAcc0IndexLogin(self):
		self.loginAccount(0)

	def selectAcc1IndexLogin(self):
		self.loginAccount(1)

	def loginAccount(self, index):
		file_object = open("user//credentials", "r")
		credentials = file_object.readlines()

		for i in credentials:
			ind = i.split(":")
			if ind[0] == str(index):
				self.Connect("{}".format(ind[1]), "{}".format(self.decode(self.__ReadSavedPassword(),ind[2]) ))
				break

	def ClearAccounts(self):
		self.loginAcc0.SetText('')
		self.loginAcc1.SetText('')
		self.loginAcc2.SetText('')
		self.loginAcc3.SetText('')

	def saveAccount(self, index):
		id = self.idEditLine.GetText()
		pwd = self.pwdEditLine.GetText()
		keypw = self.__ReadSavedPassword()
		pwd = self.encode(keypw, pwd)

		if (len(id) == 0 or len(pwd) == 0):
			self.PopupNotifyMessage("Please type ID and Password.")
			return

		try:
			file_object = open("user//credentials", "a")
		except:
			file_object = open("user//credentials", "w")

		file_objectRead = open("user//credentials", "r")
		credentials = file_objectRead.readlines()
		file_object.close()
		file_object = open("user//credentials", "w")

		for i in credentials:
			ind = i.split(":")
			if ind[0] != str(index):
				file_object.write(i)

		file_object.write("{}:{}:{}\n".format(index,id,pwd))
		file_object.close()
		file_objectRead.close()
		self.PopupNotifyMessage("Conta Guardada.")
		self.ClearAccounts()
		self.LoadAccounts()

	def delAccount(self, index):
		try:
			file_object = open("user//credentials", "a")
		except:
			file_object = open("user//credentials", "w")

		file_objectRead = open("user//credentials", "r")
		credentials = file_objectRead.readlines()
		file_object.close()
		file_object = open("user//credentials", "w")

		for i in credentials:
			ind = i.split(":")
			if ind[0] != str(index):
				file_object.write(i)

		file_object.close()
		file_objectRead.close()

		self.PopupNotifyMessage("Conta Deletada.")
		self.ClearAccounts()
		self.LoadAccounts()

 

 

I think i'm not missing anything but if you find any error let me know and i will update the post.
Probably gonna try to do some changes on the code to make it easier to add more accounts, currently it's not that hard but can be better.

Feel free to leave suggestions and help improve this!

  • Metin2 Dev 79
  • Good 19
  • muscle 1
  • Love 4
  • Love 53

Thank you ? 

Do you think would be possible to adapt this code for this version too?

So when you click F1 the first saved account will be loaded, if you click F2 will be loaded the second account, and the same thing for F3 and F4.

Now it's not working because the account are saved like this:
0 : idaccount1 : password

1 : idaccount2 : password

instead of :

idaccount1 : password

Do you know how the first index number could be ignored during the login?

 

	def OnKeyDown(self, key):
		# A fast method to login by keyboard shortcuts.
		# Create a keyboard range enum as tuple from F1 to F4.
		dikAvailableList = range(0x3B, 0x3E + 1)
		if not key in dikAvailableList:
			return

		try:
			# Creates a file object, which would be utilized to call other support methods associated with it.
			# Change old_open method with open if you get error.
			file = old_open('user//credentials', 'r')

			try:
				# Finds the given element in a list and returns its position.
				account_index = dikAvailableList.index(key)
				# Reads until EOF using readline() and returns a list containing the lines.
				account_data = file.readlines().__getitem__(account_index)
				# Returns a list of all the words in the string, using str as the separator.
				account_data = account_data.split(':')
				# Applies a function to all the items in an input list.
				account_data = map(lambda item: item.rstrip(), account_data)
				# Decode the password.
				account_data[-1] = self.decode(self.__ReadSavedPassword(), next(reversed(account_data)))

				# Connect to the server by using list [id, pw].
				self.Connect(*account_data)
			# Raised when a sequence subscript is out of range.
			except IndexError:
				pass

			# A closed file cannot be read or written any more.
			file.close()
		# Raised when an I/O operation fails for an I/O-related reason, e.g., “file not found” or “disk full”.
		except IOError:
			return

 

Edited by Cripplez
  • Metin2 Dev 1
  • Active+ Member
22 minutes ago, Cripplez said:

Thank you ? 

Do you think would be possible to adapt this code for this version too?

So when you click F1 the first saved account will be loaded, if you click F2 will be loaded the second account, and the same thing for F3 and F4

	def OnKeyDown(self, key):
		# A fast method to login by keyboard shortcuts.
		# Create a keyboard range enum as tuple from F1 to F4.
		dikAvailableList = range(0x3B, 0x3E + 1)
		if not key in dikAvailableList:
			return

		try:
			# Creates a file object, which would be utilized to call other support methods associated with it.
			# Change old_open method with open if you get error.
			file = old_open('user//credentials', 'r')

			try:
				# Finds the given element in a list and returns its position.
				account_index = dikAvailableList.index(key)
				# Reads until EOF using readline() and returns a list containing the lines.
				account_data = file.readlines().__getitem__(account_index)
				# Returns a list of all the words in the string, using str as the separator.
				account_data = account_data.split(':')
				# Applies a function to all the items in an input list.
				account_data = map(lambda item: item.rstrip(), account_data)
				# Decode the password.
				account_data[-1] = self.decode(self.__ReadSavedPassword(), next(reversed(account_data)))

				# Connect to the server by using list [id, pw].
				self.Connect(*account_data)
			# Raised when a sequence subscript is out of range.
			except IndexError:
				pass

			# A closed file cannot be read or written any more.
			file.close()
		# Raised when an I/O operation fails for an I/O-related reason, e.g., “file not found” or “disk full”.
		except IOError:
			return

 

Yes and i think it can be done in a easier way than that, making use of the functions loginAccount, i'm currently trying to implement the Sash System on my server but as soon as i'm done with it, i will do that

 

But this code might work, i didnt test it, but give it a try and let me know!

Spoiler
def OnKeyDown(self, key):
  dikAvailableList = range(0x3B, 0x3E + 1)
  if not key in dikAvailableList:
    return
  
  account_index = dikAvailableList.index(key)
  self.loginAccount(account_index)

 

 

Edited by LethalArms
  • Good 1
  • Active+ Member
15 minutes ago, Cripplez said:

Thank you again it is perfect!

just need to edit: loginAccount(account_index) -> self.loginAccount(account_index)

Fixed, glad to know it works, gonna add it on the original post thanks!

 

Also, you can easily make it show F1 - accountid, F2-accountid on the button like some servers have

 

on def LoadAccounts():

Find self.loginAcc0.SetText(ind[1])
Replace with self.loginAcc0.SetText('F1 - ' + ind[1])

Find self.loginAcc1.SetText(ind[1])
Replace with self.loginAcc1.SetText('F2 - ' + ind[1])

 

Edited by LethalArms
  • Good 1
  • 1 year later...

Thanks for sharing. I am rather new to client modding and still trying to figure out how everything is connected.

I installed Norths system as he made his guide for idiots, but I am struggling with your implementation. I followed the steps but I do not understand what to do with the icons you shared and what you mean by `root/introwindow.py`. For now I just created this file in root, but don't know if this is what you meant by this?

My error messages are only stating abort and that it happens during execution of `__LoadScript(self, fileName)` function.

Looking forward to your reply

Edited by im4x8061
  • Active+ Member
On 12/28/2024 at 1:57 PM, im4x8061 said:

Thanks for sharing. I am rather new to client modding and still trying to figure out how everything is connected.

I installed Norths system as he made his guide for idiots, but I am struggling with your implementation. I followed the steps but I do not understand what to do with the icons you shared and what you mean by `root/introwindow.py`. For now I just created this file in root, but don't know if this is what you meant by this?

My error messages are only stating abort and that it happens during execution of `__LoadScript(self, fileName)` function.

Looking forward to your reply

By bad, the introwindow file is for the interface i had at the time, the correct file is LoginWindow which normally is located on locale/yourlanguage/ui
Take in mind that my code of introwindow was set up for my interface, you will need to change the images path and positions to work with your interface but its not hard to do

On 12/31/2024 at 5:47 PM, LethalArms said:

By bad, the introwindow file is for the interface i had at the time, the correct file is LoginWindow which normally is located on locale/yourlanguage/ui
Take in mind that my code of introwindow was set up for my interface, you will need to change the images path and positions to work with your interface but its not hard to do

Thanks for the reply. I actually did try this before, but good to know, that it was correct.

I finally understood, that there is login interface by Chitra, which is why my buttons won't work.

I tried using standard buttons instead, but they are non-clickable.. Do you have any idea with this? I looked into Chitras system but all I found was the intrologin.py (which I can't use right away without ruining my channel switcher). Where are the buttons defined?

Don't use any images from : imgur, turkmmop, freakgamers, inforge, hizliresim... Or your content will be deleted without notice...
Use : https://metin2.download/media/add/

Please use https://metin2.download/ when uploading files smaller than 100MB, otherwise the approval will take longer due to manual upload.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.