Character Select / Creator


#21

:expressionless: yeah, came to the same conclusion just in this moment. Whoops. Thank you ^^


#22

This version only works for vSro 1.188 Servers

So here is my work from yesterday. Maybe my rookie python skills can help someone. Iā€™m sure someone with better python skills could do it better than I have done. Feel free to improve it :wink:

What it does is:

  • If a character is lvl 40 or above the plugin will delete this chars
  • If a char lower than 40 is available the plugin will login this char
  • If not it will create a new char and login this char then.

Two files are needed:

academy.py
from phBot import *
from packet import *
import struct
from struct import pack
from threading import Thread
from time import sleep
import string
import random


successfullCharCreated = False
loginChar = False
loginCharname = ""
charsDeleted = False
CHARACTER_TYPE = 'CH'
generalCharCount = 0

def create_character(name):
	global CHARACTER_TYPE

	if CHARACTER_TYPE == 'CH':
		c_model = get_monster_string('CHAR_CH_MAN_ADVENTURER')['model']
		chest = get_item_string('ITEM_CH_M_HEAVY_01_BA_A_DEF')['model']
		legs = get_item_string('ITEM_CH_M_HEAVY_01_LA_A_DEF')['model']
		shoes = get_item_string('ITEM_CH_M_HEAVY_01_FA_A_DEF')['model']
		weapon = get_item_string('ITEM_CH_SWORD_01_A_DEF')['model']
	elif CHARACTER_TYPE == 'EU':
		c_model = get_monster_string('CHAR_EU_MAN_NOBLE')['model']
		chest = get_item_string('ITEM_EU_M_HEAVY_01_BA_A_DEF')['model']
		legs = get_item_string('ITEM_EU_M_HEAVY_01_LA_A_DEF')['model']
		shoes = get_item_string('ITEM_EU_M_HEAVY_01_FA_A_DEF')['model']
		weapon = get_item_string('ITEM_EU_DAGGER_01_A_DEF')['model']
	else:
		log('Invalid character type (CH or EU)')
		return

	if c_model == 0 or chest == 0 or legs == 0 or shoes == 0 or weapon == 0:
		log('Could not retrieve item models')
		return

	log('Creating character with name %s and type %s' % (name, CHARACTER_TYPE))

	p = b'\x01'
	p += pack('H', len(name))
	p += name.encode('ascii')
	p += pack('I', c_model)
	p += pack('B', 0)
	p += pack('I', chest)
	p += pack('I', legs)
	p += pack('I', shoes)
	p += pack('I', weapon)
	inject_joymax(0x7007, p, False)
	sleep(1)
	inject_joymax(0x7007, b'\x02', False)

def deleteChar(chars):
	sleep(2)
	for x in chars:
		packet = stream_writer()
		packet.write_int8(3)
		packet.write_uint16(len(x))
		packet.write_ascii(x)
		inject_joymax(0x7007, packet.data, False)
		log('Delete: ' + x)
		sleep(0.2)
	sleep(2)
	inject_joymax(0x7007, b'\x02', False)

def loginChars(chars):
	global successfullCharCreated
	global generalCharCount
	global loginChar
	global loginCharname
	sleep(2)
	if len(chars) > 0:
		for x in chars:
			log('SelectCharacter: ' + x)
			loginChar = True
			loginCharname = x
			break
	else:
		log('Can not find any characters for login...Try to create new character')
		if successfullCharCreated == False and int(generalCharCount) < 4:
			name = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10))
			create_character(name)
		else:
			log('Can not create a new character. Not enough character space')
			
def event_loop():
	global loginChar, loginCharname

	if loginChar:
		loginChar = False
		select_character(loginCharname)


def handle_joymax(opcode, data):
	if opcode == 0xB007:
		packet = stream_reader(data)
		type = packet.read_uint8()
		if type == 2:
			if packet.read_uint8() == 1:
				charsToDelete = list()
				charsToLogin = list()
				global charsDeleted
				global generalCharCount
				count = packet.read_uint8()
				generalCharCount = count
				x = 0
				while x < count:
					packet.read_int32()                     #Model
					lng = packet.read_int16()
					charname = packet.read_ascii(int(lng))  #Charname
					packet.read_uint8()                     #Volume
					level = packet.read_uint8()             #Level
					packet.read_uint64()                    #EXP
					packet.read_uint16()                    #STR
					packet.read_uint16()                    #INT
					packet.read_uint16()                    #Stats
					packet.read_uint32()                    #HP
					packet.read_uint32()                    #MP
					restoreFlag = packet.read_uint8()
					if restoreFlag == 1:
					    packet.read_uint32()                #Delete Time
					packet.read_uint8()
					packet.read_uint8()
					packet.read_uint8()
					itemCount = packet.read_uint8()
					y = 0
					while y < itemCount:
					    packet.read_uint32()
					    packet.read_uint8()
					    y += 1
					avatarCount = packet.read_uint8()
					z = 0
					while z < avatarCount:
					    packet.read_uint32()
					    packet.read_uint8()
					    z += 1
					if level >= 40 and restoreFlag != 1:
					    charsToDelete.append(charname)
					elif restoreFlag != 1:
					    charsToLogin.append(charname)
					x += 1
				if  charsDeleted == False and len(charsToDelete) > 0:
					delete = Thread(target = deleteChar, args = (charsToDelete, ))
					delete.daemon = True
					delete.start()
				else:
					log('Can not find chars LvL 40 or above...Try to login a character')
					loginThread = Thread(target = loginChars, args = (charsToLogin, ))
					loginThread.daemon = True
					loginThread.start()
				charsDeleted = True
		elif type == 1:
			global successfullCharCreated
			successfullCharCreated = True
		
	return False

log('[AcademyLoginHelper]v1.0 Loaded - Credits: CharCreation by Ryan')
packet.py
import struct
import array

class stream_reader(object):

	index = 0
	data = None
	size = 0

	def __init__(self, data, index=0):
		self.reset(data, index)

	def reset(self, data, index=0):
		self.data = data

		self.size = self.data.__len__()
		self.seek_set(index)

	def bytes_left(self):
		return self.size - self.index

	def seek_forward(self, count):
		if self.index + count > self.size:
			raise Exception('index would be past end of stream')
		self.index += count

	def seek_backward(self, count):
		if self.index - count < 0:
			raise Exception('index would be < 0 if seeked further back')
		self.index -= count

	def seek_set(self, index):
		if index > self.size or index < 0:
			raise Exception('invalid index')
		self.index = index

	def read_int8(self):
		if self.index + 1 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('b', self.data, self.index)[0]
		self.index += 1
		return unpacked

	def read_uint8(self):
		if self.index + 1 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('B', self.data, self.index)[0]
		self.index += 1
		return unpacked

	def read_int16(self):
		if self.index + 2 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('h', self.data, self.index)[0]
		self.index += 2
		return unpacked

	def read_uint16(self):
		if self.index + 2 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('H', self.data, self.index)[0]
		self.index += 2
		return unpacked

	def read_int32(self):
		if self.index + 4 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('i', self.data, self.index)[0]
		self.index += 4
		return unpacked

	def read_uint32(self):
		if self.index + 4 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('I', self.data, self.index)[0]
		self.index += 4
		return unpacked

	def read_int64(self):
		if self.index + 8 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('q', self.data, self.index)[0]
		self.index += 8
		return unpacked

	def read_uint64(self):
		if self.index + 8 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('Q', self.data, self.index)[0]
		self.index += 8
		return unpacked

	def read_float(self):
		if self.index + 4 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('f', self.data, self.index)[0]
		self.index += 4
		return unpacked

	def read_double(self):
		if self.index + 8 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('d', self.data, self.index)[0]
		self.index += 8
		return unpacked

	def read_char(self):
		if self.index + 1 > self.size:
			raise Exception('past end of stream')
		unpacked = struct.unpack_from('c', self.data, self.index)[0]
		self.index += 1
		return unpacked

	def read_ascii(self, length):
		if self.index + length > self.size:
			raise Exception('past end of stream')
		string = struct.unpack_from(str(length) + 's', self.data, self.index)[0]
		self.index += length
		return string.decode('ascii', 'replace')

	'''
	def read_utf8(self, length):
		if self.index + length > self.size:
			raise Exception('past end of stream')
		string = struct.unpack_from(str(length) + 's', self.data, self.index)[0]
		self.index += length
		return string.decode('utf-8')
	'''

	def read_utf16(self, length):
		length *= 2
		if self.index + length > self.size:
			raise Exception('past end of stream')
		string = struct.unpack_from(str(length) + 's', self.data, self.index)[0]
		self.index += length
		return string.decode('utf-16le')

	def read_utf32(self, length):
		length *= 4
		if self.index + length > self.size:
			raise Exception('past end of stream')
		string = struct.unpack_from(str(length) + 's', self.data, self.index)[0]
		self.index += length
		return string.decode('utf-32le')

class stream_writer(object):

	data = array.array('B')
	index = 0
	size = 0

	def __init__(self, data=None):
		self.reset(data)

	def reset(self, data=None):
		if type(data) == array.array:
			self.data = data
		elif type(data) == list:
			self.data = array.array('B', data)
		elif data == None:
			self.data = array.array('B')
		else:
			raise Exception('incorrect data type was used to reset the class')

		self.size = self.data.__len__()
		self.seek_end()

	def tostring(self):
		return self.data.tostring()

	def tolist(self):
		return self.data.tolist()

	def tofile(self, f):
		return self.data.tofile(f)

	def toarray(self):
		return self.data

	def seek_forward(self, count):
		if self.index + count > self.size:
			raise Exception('index would be past end of stream')
		self.index += count

	def seek_backward(self, count):
		if self.index - count < 0:
			raise Exception('index would be < 0 if seeked further back')
		self.index -= count

	def seek_set(self, index):
		if index > self.size or index < 0:
			raise Exception('invalid index')
		self.index = index

	def seek_end(self):
		self.index = self.size

	def write_int8(self, val):
		self.__append(struct.pack('b', val))

	def write_uint8(self, val):
		self.__append(struct.pack('B', val))

	def write_int16(self, val):
		self.__append(struct.pack('h', val))

	def write_uint16(self, val):
		self.__append(struct.pack('H', val))

	def write_int32(self, val):
		self.__append(struct.pack('i', val))

	def write_uint32(self, val):
		self.__append(struct.pack('I', val))

	def write_int64(self, val):
		self.__append(struct.pack('q', val))

	def write_uint64(self, val):
		self.__append(struct.pack('Q', val))

	def write_float(self, val):
		self.__append(struct.pack('f', val))

	def write_double(self, val):
		self.__append(struct.pack('d', val))

	def write_char(self, val):
		self.__append(struct.pack('c', val))

	def write_ascii(self, val):
		self.__append(bytes(val, 'ascii', 'replace'))

	'''
	def write_utf8(self, val):
		self.__append(bytes(val, 'utf-8'))
	'''

	def write_utf16(self, val):
		self.__append(val.encode('utf-16le'))

	def write_utf32(self, val):
		self.__append(val.encode('utf-32le'))

	def write(self, val):
		self.__append(val)

	def __append(self, packed):
		for x in packed:
			self.data.insert(self.index, x)
			self.index += 1
			self.size += 1

Have fun :grinning:


#23

One issue is the character listing is different on different locales. So whatever you coded it for it will only work on that type of server.