Character Select / Creator

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

1 Like

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:

6 Likes

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.

2 Likes

Iā€™m a sbot user and I want to move to phbot because of this academy plugin . I downloaded the phbot , I made all the settings , I even learned how to add quests in script , now I need to learn about this . What should I do after downloading python ?

Use the installer and enable the ā€œPluginsā€ option which will install Python for you; then copy the plugin code into SublimeText/Notepad++ and save it into the Plugins folder.

https://phbot.org/phBot%20Installer.exe

welcom on the project hax =)

here u have some help for add your .py on your folder

1 Like

because it does not select characters, DC becomes

Are you using the plugin correctly? It does select characters.

creates the character,does not select the character it creates

EU do you have a bug?
from phBot import *
from struct import pack
import string
import random

CHARACTER_TYPE = ā€˜EUā€™

create = False
request_listing = False

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)

request_listing = True

def character_listing(args):
global create
if len(args) == 0:
create = True
else:
deleting = 0
for name in enumerate(args):
if not name.startswith(ā€™*ā€™):
log(ā€˜Selecting %sā€™ % name)
select_character(name)
deleting = -1
break
else:
deleting += 1
if deleting != -1 and deleting < 4:
create = True
else:
log(ā€˜Account full, all your chars are at deleting modeā€™)
return 0

def event_loop():
global create, request_listing

if create:
    create = False
    name = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10))
    create_character(name)
elif request_listing:
    request_listing = False
    inject_joymax(0x7007, b'\x02', False)

log(ā€™[%s] Loadedā€™ % name)

I do not understand why you copy and pasted the entire plugin.

1 Like

EU do you have a bug?

Use this: Character Select / Creator

Iā€™m using that too ,creates the character,does not select the character it creates dc 2. he chooses the character he creates when he logs in.

I just changed something which may work.

Iā€™m using it wrong?

Yes, reinstall that plugin.

bro, i have one problem, i make condition on current lvl 41>disconnect but any time with only 1 char on account the pluguin said ā€œCan not create a new character. Not enough character spaceā€ you can revise this method ? thanks alot

is the code working? it gives me the error.

do you have char in delete time on this acount ?