I want to make simple exchange between all my side chars. Any way to do that without party?
xchanger wont work in trsro can u fix brother
1 Like
Code
from phBot import *
import QtBind
import struct
import json
import os
import time
from enum import Enum
from datetime import datetime, timedelta
pName = 'xChanger'
pVersion = '1.2'
pUrl = 'https://raw.githubusercontent.com/JellyBitz/phBot-xPlugins/master/xChanger.py'
# ______________________________ Initializing ______________________________ #
# globals
character_data = None
exchange_initiator = False
ExchangeStatus = ''
timer = datetime.now()
# opcodes
class ExchangeCode(Enum):
CLIENT_GAME_PETITION_RESPONSE = 0x3080
SERVER_GAME_PETITION_REQUEST = 0x3080
SERVER_EXCHANGE_STARTED = 0x3085
SERVER_EXCHANGE_CONFIRMED = 0x3086
SERVER_EXCHANGE_APPROVED = 0x3087
SERVER_EXCHANGE_CANCELED = 0x3088
CLIENT_EXCHANGE_START_REQUEST = 0x7081
CLIENT_EXCHANGE_CONFIRM_REQUEST = 0x7082
CLIENT_EXCHANGE_APPROVE_REQUEST = 0x7083
CLIENT_EXCHANGE_CANCEL_REQUEST = 0x7084
SERVER_EXCHANGE_START_RESPONSE = 0xB081
SERVER_EXCHANGE_CONFIRM_RESPONSE = 0xB082
SERVER_EXCHANGE_APPROVE_RESPONSE = 0xB083
SERVER_EXCHANGE_CANCEL_RESPONSE = 0xB084
# Initializing GUI
gui = QtBind.init(__name__,pName)
_x=6
_y=9
QtBind.createLabel(gui,'* Exchangers list (Party required)',_x,_y)
_y+=20
tbxExchangerName = QtBind.createLineEdit(gui,"",_x,_y,100,20)
QtBind.createButton(gui,'btnAddExchanger_clicked'," Add ",_x+101,_y-2)
_y+=20
lvwExchangers = QtBind.createList(gui,_x,_y,176,60)
_y+=60
QtBind.createButton(gui,'btnRemExchanger_clicked'," Remove ",_x+49,_y-2)
_y+=20 + 10
cbxReplyAccept = QtBind.createCheckBox(gui,'checkbox_changed','Auto accept reply',_x,_y)
_y+=20
cbxReplyApprove = QtBind.createCheckBox(gui,'checkbox_changed','Auto approve reply',_x,_y)
_x+=185
_y=9
cbxAcceptAll = QtBind.createCheckBox(gui,'checkbox_changed','Accept all exchange invitations',_x,_y)
_y+=20
cbxAcceptParty = QtBind.createCheckBox(gui,'checkbox_changed','Accept all exchange invitations from party member',_x,_y)
# ______________________________ Methods ______________________________ #
# Return folder path
def get_path():
return get_config_dir()+pName+"\\"
# Return character configs path (JSON)
def get_config():
return get_path()+character_data['server'] + "_" + character_data['name'] + ".json"
# Check if character is ingame
def is_joined():
global character_data
character_data = get_character_data()
if not (character_data and "name" in character_data and character_data["name"]):
character_data = None
return character_data
# Load default configs
def load_default_config():
# Clear data
QtBind.setChecked(gui,cbxAcceptAll,False)
QtBind.setChecked(gui,cbxAcceptParty,False)
QtBind.setChecked(gui,cbxReplyAccept,True)
QtBind.setChecked(gui,cbxReplyApprove,True)
QtBind.clear(gui,lvwExchangers)
# Save all config
def save_configs():
# Save if data has been loaded
if is_joined():
# Save all data
data = {}
data["AcceptAll"] = QtBind.isChecked(gui,cbxAcceptAll)
data["AcceptParty"] = QtBind.isChecked(gui,cbxAcceptParty)
data["ReplyAccept"] = QtBind.isChecked(gui,cbxReplyAccept)
data["ReplyApprove"] = QtBind.isChecked(gui,cbxReplyApprove)
data["Exchangers"] = QtBind.getItems(gui,lvwExchangers)
# Overrides
with open(get_config(),"w") as f:
f.write(json.dumps(data, indent=4, sort_keys=True))
log("Plugin: "+pName+" configs has been saved")
# Loads all config previously saved
def load_configs():
load_default_config()
if is_joined():
# Check config exists to load
if os.path.exists(get_config()):
data = {}
with open(get_config(),"r") as f:
data = json.load(f)
# Load data
if "AcceptAll" in data and data['AcceptAll']:
QtBind.setChecked(gui,cbxAcceptAll,True)
if "AcceptParty" in data and data['AcceptParty']:
QtBind.setChecked(gui,cbxAcceptParty,True)
if "ReplyAccept" in data and not data['ReplyAccept']:
QtBind.setChecked(gui,cbxReplyAccept,False)
if "ReplyApprove" in data and not data['ReplyApprove']:
QtBind.setChecked(gui,cbxReplyApprove,False)
if "Exchangers" in data:
for charName in data["Exchangers"]:
QtBind.append(gui,lvwExchangers,charName)
# Called when any checkbox value changed
def checkbox_changed(newValue):
save_configs()
# Return True if text exist at the list
def string_in_list(vString,vList,ModeSensitive=False):
if not ModeSensitive:
vString = vString.lower()
for i in range(len(vList)):
if not ModeSensitive:
vList[i] = vList[i].lower()
if vList[i] == vString:
return True
return False
# Add leader to the list
def btnAddExchanger_clicked():
# Check in game data
if character_data:
player = QtBind.text(gui,tbxExchangerName)
# Player nickname it's not empty and not added
if player and not string_in_list(player,QtBind.getItems(gui,lvwExchangers)):
QtBind.append(gui,lvwExchangers,player)
save_configs()
# saved successfully
QtBind.setText(gui,tbxExchangerName,"")
log('Plugin: Exchanger added ['+player+']')
# Remove leader selected from list
def btnRemExchanger_clicked():
# Check in game data
if character_data:
selectedItem = QtBind.text(gui,lvwExchangers)
if selectedItem:
QtBind.remove(gui,lvwExchangers,selectedItem)
# saved successfully
save_configs()
log("Plugin: Exchanger removed ["+selectedItem+"]")
# Return character name from player ID but only if is in party
def get_charname(UniqueID):
# Checking if UID is mine
if UniqueID == character_data['player_id']:
return character_data['name']
# Load players from party
players = get_party()
# Check UID existence
if players:
for key, player in players.items():
if player['player_id'] == UniqueID:
return player['name']
return ""
# Send the response to the last game petition
def Inject_GamePetitionResponse(Accept,Type):
if Accept:
p = b'\x01\x01'
else:
# Party Invitation or Party Creation
if Type == 2 or Type == 3:
p = b'\x02\x0C\x2C'
# Default
else:
p = b'\x01\x00'
inject_joymax(ExchangeCode.CLIENT_GAME_PETITION_RESPONSE.value,p,False)
# ______________________________ Events ______________________________ #
# Called when the character enters the game world
def joined_game():
load_configs()
def handle_silkroad(opcode, data):
global exchange_initiator
global ExchangeStatus
# determine the initiator (player1) of the exchange
if opcode == ExchangeCode.CLIENT_EXCHANGE_START_REQUEST.value:
global timer
exchange_initiator = True
# add delay between successive exchanges, otherwise the exchange will fail, due to packet flooding?
while (datetime.now() - timer < + timedelta(seconds=6)):
time.sleep(1)
return True
# All packets received from game server will be passed to this function
# Returning True will keep the packet and False will not forward it to the game client
def handle_joymax(opcode, data):
if opcode == ExchangeCode.SERVER_GAME_PETITION_REQUEST.value:
t = data[0]
# petition type
if t == 1: # exchange
# Accept everyone
if QtBind.isChecked(gui,cbxAcceptAll):
Inject_GamePetitionResponse(True,t)
return True
# Try to extract nickname
entityID = struct.unpack_from('<I', data, 1)[0]
charName = get_charname(entityID)
# accept if party member
if QtBind.isChecked(gui,cbxAcceptParty):
party = get_party()
if party:
for pid, player in party.items():
if player['player_id'] == entityID:
Inject_GamePetitionResponse(True,t)
return True
# Accept if nickname is found in list
if string_in_list(charName,QtBind.getItems(gui,lvwExchangers)):
Inject_GamePetitionResponse(True,t)
return True
# Update depending exchange status
global ExchangeStatus
global exchange_initiator
global timer
if opcode == ExchangeCode.SERVER_EXCHANGE_STARTED.value:
ExchangeStatus = 'STARTED' #player2
elif opcode == ExchangeCode.SERVER_EXCHANGE_START_RESPONSE.value:
if data[0] == 1: # success
ExchangeStatus = 'STARTED' #player1
# apply confirmations
elif opcode == ExchangeCode.SERVER_EXCHANGE_CONFIRMED.value:
# confirm exchange if player2
if ExchangeStatus == 'STARTED' and not exchange_initiator:
if QtBind.isChecked(gui,cbxReplyAccept):
inject_joymax(ExchangeCode.CLIENT_EXCHANGE_CONFIRM_REQUEST.value,b'',False)
# approve exchange if player1
if ExchangeStatus == 'CONFIRMED' and exchange_initiator:
if QtBind.isChecked(gui,cbxReplyApprove):
inject_joymax(ExchangeCode.CLIENT_EXCHANGE_APPROVE_REQUEST.value,b'',False)
elif opcode == ExchangeCode.SERVER_EXCHANGE_CONFIRM_RESPONSE.value:
if data[0] == 1: # success
ExchangeStatus = 'CONFIRMED'
# reply if is required
if QtBind.isChecked(gui, cbxReplyApprove):
# approve exchange
if not exchange_initiator:
inject_joymax(ExchangeCode.CLIENT_EXCHANGE_APPROVE_REQUEST.value,b'',False)
elif opcode == ExchangeCode.SERVER_EXCHANGE_APPROVE_RESPONSE.value:
if data[0] == 1: # success
ExchangeStatus = 'APPROVED'
elif opcode == ExchangeCode.SERVER_EXCHANGE_APPROVED.value or opcode == ExchangeCode.SERVER_EXCHANGE_CANCELED.value:
exchange_initiator = False
ExchangeStatus = ''
timer = datetime.now()
elif opcode == ExchangeCode.SERVER_EXCHANGE_CANCEL_RESPONSE.value:
if data[0] == 1: # success
exchange_initiator = False
timer = datetime.now()
ExchangeStatus = ''
return True
# Plugin loaded
log('Plugin: '+pName+' v'+pVersion+' succesfully loaded')
# Check folder existence
if os.path.exists(get_path()):
# RELOAD plugin support
load_configs()
else:
# Create configs folder
os.makedirs(get_path())
log('Plugin: '+pName+' folder has been created')
- Bugfix: 2 players with both options enabled failed to exchange
- Feature: Auto-accept exchange from all party members
2 Likes