SamDev-Coder Posted October 18, 2017 Posted October 18, 2017 Hello mates, Since all my previous posts was for H5 Chronicle so i think i’ll start post some content for Interlude Chronicle, so here is my first Interlude guide, but after all Java is Java, no matter Interlude, H5, … etc all are just Java files The Server Pack i’m gonna use in this guide will be ( aCis Rev.360 [Free Version] ), and for the IDE will use NetBeans, of course you can use Eclipse it’s just personal preferences. ---------------------------------------------------------------------------------- Requirements : – I expect that you have aCis Source Pack Installed on your computer or server – We won’t go through the process of installing and starting server, since there are a lot of good guides about this here. – You need a Java IDE (Eclipse, Netbeans, IntelliJ or any else) i’ll use NetBeans but you can use Any. – Stay Relaxed, No Stress, Coding is just Entertaining. ---------------------------------------------------------------------------------- – How things happen in Game ? Well In Lineage 2 Every thing has two sides (Client and Server), whatever you do in Client (Game) it will automatically send a request to server, then server analyze this request, start processing it and response with a response . That data the fly between Client and Server called Packets , This Packets can be a Request Packet or Response Packet, and each Packet has special code or identifier, if you click here you will find the main Packets for Interlude. For Example : You Click on Community or ALT+B, your game client will send a packet to server called RequestShowboardwith code 57, this is sent from Client to Server, so it’s ClientPacket. So if we want to do any special thing when Player try to show the community board we need to find this packet code which since it’s a ClientPacket then it will be in that package : net.sf.l2j.gameserver.network.clientpackets When Server Analyze that request and see things like if it’s enabled … etc it will reply with another Packet which is ShowBoard with code 6E (if board enabled ofc), and this is sent from Server to Client, so it’s a ServerPacket, and since it’s a ServerPacket so we can find it in that package : net.sf.l2j.gameserver.network.serverpackets , and we can expect that this class or java file will be responsible for what will be shown on the board. By now we know how to find where magic happens, but what about more power ? ---------------------------------------------------------------------------------- What is Bypass and how it works ? Whenever you click on a button or a link for an NPC or even in Community Board this buttons and links meant to do something when a player click on it, but how it happens ? and how server knows which button player clicked and what should happen after that ? Well if you try to show the code of any l2j html file that contain a link or a button, for example i’ll view source code of aCis main buffer by @Tryskell, which located in gamserver/data/html/mods/buffer/50008.htm <html><body> <center><img src="L2UI_CH3.herotower_deco" width=256 height=32 /></center><br> Hello stranger!<br> I'm Tryskell. Yup, you're right, I created an avatar of myself and decided to share some of my powerful buffs in order to improve your pew-pew-bum-zap abilities.<br> What can I do for you ? Do you want to hear <font color="LEVEL"><a action="bypass -h npc_%objectId%_Chat 9">my story</a></font> ?<br> <center> <a action="bypass -h npc_%objectId%_support player">Magic support</a><br> <a action="bypass -h npc_%objectId%_manageschemes">Manage my schemes</a><br1> <a action="bypass -h npc_%objectId%_editschemes none none">Edit my schemes</a><br> <a action="bypass -h npc_%objectId%_heal">Heal me and my pet</a><br1> <a action="bypass -h npc_%objectId%_cleanup">Cleanup all effects on me and my pet</a> </center> </body></html> Here you can see the attribute action contains something like : Quote bypass -h npc_%objectId%_Chat 9 let’s split this action in parts and see what every part do : bypass : tells your client to send a packet RequestBypassToServer with OP Code 0x21 (0x identify a hex decimal > a numeric base 16 value ) -h : tells your client to hide the window on click event npc_%objectId%_Chat 9 : is the command that will be bypassed to server So if we wanna make a bypass to server we just wanna set the action of the link (<a>) or button to something like : “bypass -h command” or just “bypass command” But actually we still dunno what commands already available or if we can create a new custom ….. ----------------------------------------------------------- Track the Bypass down : I can just give you a list of possible bypass commands but i hate limitations, so i’ll give you a quick guide about how to find them and learn about what they do all on your own and then will give you some examples, also this will help you work with almost any pack or chronicle, so let’s see how it works. First packets sent to server will be sent to a class called L2GamePacketHandler.java, which in aCis it’s located at : net.sf.l2j.gameserver.network , and then this class will redirect that packet to the proper Handler or class depending on the received packet OPCode, and since we know that RequestBypassToServer have the OPCode of 0x21 so what about search about this in that Java File : As we can see it call a class called RequestBypassToServer, if we click on this class name : For Netbeans : Right Click > Navigate > Declaration (or CTRL+B) For Eclipse : Right Click > Go To > Declaration It will open that file we will find a part of the code like this (i removed some part just to be easier to read) : if (_command.startsWith("admin_")) { .... } else if (_command.startsWith("player_help ")) { .... } else if (_command.startsWith("npc_")) { .... } else if (_command.startsWith("manor_menu_select?")) { .... } else if (_command.startsWith("bbs_") || _command.startsWith("_bbs") || _command.startsWith("_friend") || _command.startsWith("_mail") || _command.startsWith("_block")) { .... } else if (_command.startsWith("Quest ")) { .... } else if (_command.startsWith("_match")) { .... } else if (_command.startsWith("_diary")) { .... } else if (_command.startsWith("arenachange")) { .... } So as we can see the current supported bypasses are commands that started with : Quote admin_ , player_help, npc_, manor_menu_select?, bbs_, _bbs, _friend, _mail, _block, Quest,_match, _diary, arenachange Of course you can add another main custom command by adding another “else if” statement or even create a bypass handler or replace this one with an extended one. Why we don’t go further and inspect how this npc_ thing work ….. here the code of that section : else if (_command.startsWith("npc_")) { if (!activeChar.validateBypass(_command)) return; int endOfId = _command.indexOf('_', 5); String id; if (endOfId > 0) id = _command.substring(4, endOfId); else id = _command.substring(4); try { final L2Object object = L2World.getInstance().getObject(Integer.parseInt(id)); if (object != null && object instanceof L2Npc && endOfId > 0 && ((L2Npc) object).canInteract(activeChar)) ((L2Npc) object).onBypassFeedback(activeChar, _command.substring(endOfId + 1)); activeChar.sendPacket(ActionFailed.STATIC_PACKET); } catch (NumberFormatException nfe) { } } The first part can be lil confusing if you’re not very familiar with java, so lemme explain it to you . That part looking for a number that comes after npc_ that followed by an “_” for example : npc_123456 , and before we saw that command npc_%objectId%_Chat 9 , so here we expect that %objectId% is a number and yes it is since server will automatically replace %objectId% with a unique numeric identifier for the NPC you’re talking to, and it’s not the id you use to spawn it, since you may spawn that npc multiple times so server needs to identify each one of them. So for now we know that command is used like this : Quote npc_%objectId%_COMMAND Later in this code we will find that part : final L2Object object = L2World.getInstance().getObject(Integer.parseInt(id)); if (object != null && object instanceof L2Npc && endOfId > 0 && ((L2Npc) object).canInteract(activeChar)) ((L2Npc) object).onBypassFeedback(activeChar, _command.substring(endOfId + 1)); activeChar.sendPacket(ActionFailed.STATIC_PACKET); Also let me explain this for you, this code will search the L2World for something with that unique identifier and if found it will use it as L2Npc object and call the method onBypassFeedback to process the command in our case the command will be Chat 9. So Lets Go to declaration of that L2Npc class (as we learned how before), and lets see what this onBypassFeedback do we will find something like this : if (command.equalsIgnoreCase("TerritoryStatus")) { .... } else if (command.startsWith("Quest")) { .... } else if (command.startsWith("Chat")) { .... } else if (command.startsWith("Link")) { .... } else if (command.startsWith("Loto")) { .... } else if (command.startsWith("CPRecovery")) { .... } else if (command.startsWith("SupportMagic")) { .... } else if (command.startsWith("multisell")) { .... } else if (command.startsWith("exc_multisell")) { .... } else if (command.startsWith("Augment")) { .... } else if (command.startsWith("EnterRift")) { .... } else if (command.startsWith("ChangeRiftRoom")) { .... } else if (command.startsWith("ExitRift")) { .... } As we can see we can now expect that we can use npc_ commands as follow : Quote npc_%objectId%_TerritoryStatus npc_%objectId%_Quest npc_%objectId%_Chat npc_%objectId%_Link npc_%objectId%_Loto npc_%objectId%_CPRecovery npc_%objectId%_SupportMagic npc_%objectId%_multisell npc_%objectId%_exc_multisell npc_%objectId%_Augment npc_%objectId%_EnterRift npc_%objectId%_ChangeRiftRoom npc_%objectId%_ExitRift But in the previous example there were number 9 in the end as npc_%objectId%_Chat 9, what this would mean ? well here is the code of Chat command : else if (command.startsWith("Chat")) { int val = 0; try { val = Integer.parseInt(command.substring(5)); } catch (IndexOutOfBoundsException ioobe) {} catch (NumberFormatException nfe) {} showChatWindow(player, val); } As we can see here it will look for that number (9 in our case) and pass it to a method called showChatWindow , which will look for an htm file with same npc id ended with -9 for example this npc id is 50008 so it will looks for 50008-9.htm , and yes it’s right : Now we know how to track things down but one thing left, which is what if you found an bypass in an npc which you cannot find in L2Npc Class, well some npc don’t directly based on L2Npc type but sometimes it’s based on something else that based on L2Npc, for example this NPC in our example has a bypass like : Quote bypass -h npc_%objectId%_heal this “heal” command not exist but if we go to our npcs folder in path of gameserver/data/xml/npcs , and we opened the file 50000-50099.xml since our npc id is 50008 and look into that npc definition you will find this : As you can see it’s based on L2Buffer, not L2Npc directly, so we can expect that this class called L2Buffer will contain a method called onBypassFeedback and it will contain an implementation for this “heal” command, but wait a second … you’ll never find a class called L2Buffer, simply because in aCis Packs L2Buffer will redirect to a class called L2BufferInstance and L2BlaBla will redirect to L2BlaBlaInstance, it’s just how aCis simplify things, so just keep that in mind we looking for L2BufferInstance … If we go to that class which is located in the package of : net.sf.l2j.gameserver.model.actor.instance (btw this package contains a lot of interesting stuff) we will find that part inside the onBypassFeedback method : Yay , it’s here, so if we want to use this command on another npc we have 3 options : – Add this code to the main RequestBypassToServer – Set the NPC type to L2Buffer – Create a new NPC instance and implement this on it Or we can even add it to community board :D ------------------------------------------------------------------- Do you learned something from this guide ? Personally i don’t learn any thing without getting my hands into it in real action, so why not create something real, like a multi function npc and as a bonus we will create a new NPC type as well, also we can work a bit with Community Board . . . let’s go friends ------------------------------------------------------------------- 1.1 : Create a new NPC Type : Let’s Create a new Class in the package net.sf.l2j.gameserver.model.actor.instance, and i’ll call it L2MultiFunctionInstance (Name it whatever u want but don’t forget suffix it with Instance) , Just right click on the package and New > Java Class : Now i’ll make it extends the L2NpcInstance or you can use L2Npc class only, but L2NpcInstance has cool implementation so i’ll go for it, and now Server will know it’s an NPC, but you may want for example extends L2BufferInstance to take benefit of Buffer commands or anything else that considered as NPC, but i’ll go pure in this guide , and to simplify thing and to not go off topic i’ll create a simple class code for you which you can use it and i’ll highlight the important parts : //TODO: Add your Custom Bypasses Here At this place we can add our custom bypass commands “data/html/custom/multifunction/” this is where our .htm files will be located for npcs with this type ------------------------------------------------------------- 1.1 Create the Actual NPC So First let’s create our NPC, i’ll add it Below Tryskell NPC so it will be in file 50000-50099.xml : <npc id="50009" idTemplate="31324" name="Relina" title="Multifunction NPC"> <set name="level" val="70"/> <set name="radius" val="8"/> <set name="height" val="23"/> <set name="rHand" val="316"/> <set name="lHand" val="0"/> <set name="type" val="L2MultiFunction"/> <set name="exp" val="0"/> <set name="sp" val="0"/> <set name="hp" val="2444.46819"/> <set name="mp" val="1345.8"/> <set name="hpRegen" val="7.5"/> <set name="mpRegen" val="2.7"/> <set name="pAtk" val="688.86373"/> <set name="pDef" val="295.91597"/> <set name="mAtk" val="470.40463"/> <set name="mDef" val="216.53847"/> <set name="crit" val="4"/> <set name="atkSpd" val="253"/> <set name="str" val="40"/> <set name="int" val="21"/> <set name="dex" val="30"/> <set name="wit" val="20"/> <set name="con" val="43"/> <set name="men" val="20"/> <set name="corpseTime" val="7"/> <set name="walkSpd" val="50"/> <set name="runSpd" val="120"/> <set name="dropHerbGroup" val="0"/> <ai type="DEFAULT" ssCount="0" ssRate="0" spsCount="0" spsRate="0" aggro="0" canMove="true" seedable="false"/> <skills> <skill id="4045" level="1"/> <skill id="4416" level="16"/> </skills> </npc> Note that it has id of 50009 and type of L2MultiFunction (without Instance) ------------------------------------------------------------ 1.2 Create the html files Now the html part, we gonna create a folder called custom inside html folder and inside it will create multifunction folder which it’s where we gonna have our html files, First we need the main htm which will be on our created folder with name 50009.htm : <html> <body> <br> <center> <img src="L2UI_CH3.herotower_deco" width=256 height=32 /> <font color="D5C003">Hello Player</font><br> <font color="DDDEC0">I'm the Multi Function NPC with Awesome Customs</font><br> <button value="Open Another Chat" action="bypass -h npc_%objectId%_Chat 1" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Show a Multi Sell" action="bypass -h npc_%objectId%_multisell 50009" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Multisell [Inv.Maintain]" action="bypass -h npc_%objectId%_exc_multisell 50009" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Community Board" action="bypass _bbshome" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Acumen Buff - No Close" action="bypass npc_%objectId%_giveAcumen" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Teleport To Giran" action="bypass -h npc_%objectId%_gotogiran" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Augment" action="bypass -h npc_%objectId%_Augment 1" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <button value="Warehouse" action="bypass npc_%objectId%_warehouse_deposit" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <img src="L2UI_CH3.herotower_deco" width=256 height=32 /> </center> </body> </html> Thanks to @Sinister Smile for the buttons, and to make it work, you need to have this file > Download Also we will need another htm file for testing which will be named 50009-1.htm : <html> <body> <br> <center> <img src="L2UI_CH3.herotower_deco" width=256 height=32 /><br > <font color="D5C003">Hello Once Again</font><br> <font color="DDDEC0">This is another Chat of me</font><br> <button value="Back to Main" action="bypass -h npc_%objectId%_Chat 0" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> <img src="L2UI_CH3.herotower_deco" width=256 height=32 /> </center> </body> </html> ----------------------------------------------------------------- 1.3 Create the Multisell File I’ll Create a multisell file with name 50009.xml in multisell folder : <?xml version='1.0' encoding='utf-8'?> <list maintainEnchantment="true"> <item> <production id="6608" count="1"/> <ingredient id="6579" count="1" /> </item> <item> <production id="5643" count="1" /> <ingredient id="151" count="1"/> </item> </list> --------------------------------------------------------------- 1.4 Start Server (If not Yet) and let’s Test Each one Open Another Chat : Action : bypass -h npc_%objectId%_Chat 1 This will Open the htm file called 50009-1.htm Show Multi Sell : Action : bypass -h npc_%objectId%_multisell 50009 This will Show a Multisell with id of 50009, and will show all items even items player can’t buy Multi Sell [Inv.Maintain] : Action : bypass -h npc_%objectId%_exc_multisell 50009 This will Show a Multisell with id of 50009, and will show only items player can buy (have the required items for it) Community Board : Action : bypass _bbshome Will Show Community Board if enabled, you can also use : Quote _bbshome, _bbsloc, _bbsclan, _bbsmemo, _bbsmail, _maillist_0_1_0_, _friend, _block, _bbstopics, _bbsposts Augment : Action : bypass -h npc_%objectId%_Augment 1 Will Show Augment Panel, also you can use bypass -h npc_%objectId%_Augment 2, to show Remove Augment Panel Quote Buff Me, Teleport To Giran, Warehouse will do nothing since this bypasses is not available by default so we need to implement them first ----------------------------------------------------------- 1.5 Implement Missed Bypasses : So Now we need to add our custom bypasses to the NPC Type which are : giveAcument, gotogiran, warehouse_deposit Let’s get back to Java and inside onBypassFeedback in the Class we just created we will add this : if(command.equalsIgnoreCase("giveAcumen")) { SkillTable.getInstance().getInfo(1085, SkillTable.getInstance().getMaxLevel(1085)).getEffects(player, player); } else if(command.equalsIgnoreCase("gotogiran")) { player.teleToLocation(82698,148638,-3473,0); } else if(command.equalsIgnoreCase("warehouse_deposit")) { player.sendPacket(ActionFailed.STATIC_PACKET); player.setActiveWarehouse(player.getWarehouse()); player.tempInventoryDisable(); player.sendPacket(new WarehouseDepositList(player, WarehouseDepositList.PRIVATE)); } else { super.onBypassFeedback(player, command); } ------------------------------------------------------------------ 1.6 Let’s Test this When we test this everything will be find except one thing, which is Warehouse, it will show deposit window, but when you confirm, it will not move items to warehouse, since in client packet of SendWareHouseDepositList it will check if the last npc was a Warehouse and our npc is not warehouse : And to Fix this it simple, just we can add another method to our custom NPC class which will make it pretend as being warehouse : @Override public boolean isWarehouse() { return true; } So we end up with this code : package net.sf.l2j.gameserver.model.actor.instance; import net.sf.l2j.gameserver.cache.HtmCache; import net.sf.l2j.gameserver.datatables.SkillTable; import net.sf.l2j.gameserver.model.actor.template.NpcTemplate; import net.sf.l2j.gameserver.network.serverpackets.ActionFailed; import net.sf.l2j.gameserver.network.serverpackets.WarehouseDepositList; /** * Multi Function NPC Instance * * Made for MxC Bypasses Guide * * @author Sam Dev */ public class L2MultiFunctionInstance extends L2NpcInstance { public L2MultiFunctionInstance(int objectId, NpcTemplate template) { super(objectId, template); } @Override public void onBypassFeedback(L2PcInstance player, String command) { if(command.equalsIgnoreCase("giveAcumen")) { SkillTable.getInstance().getInfo(1085, SkillTable.getInstance().getMaxLevel(1085)).getEffects(player, player); } else if(command.equalsIgnoreCase("gotogiran")) { player.teleToLocation(82698,148638,-3473,0); } else if(command.equalsIgnoreCase("warehouse_deposit")) { player.sendPacket(ActionFailed.STATIC_PACKET); player.setActiveWarehouse(player.getWarehouse()); player.tempInventoryDisable(); player.sendPacket(new WarehouseDepositList(player, WarehouseDepositList.PRIVATE)); } else { super.onBypassFeedback(player, command); } } @Override public boolean isWarehouse() { return true; } @Override public String getHtmlPath(int npcId, int val) { String filename = "data/html/custom/multifunction/" + npcId + ((val == 0) ? "" : "-"+ val ) + ".htm"; return (HtmCache.getInstance().isLoadable(filename)) ? filename : "data/html/npcdefault.htm"; } } By Now we have our Multi Function NPC, But Guess what .... we have a Bonus in this Guide . ----------------------------------------------------------------- 2.0 The Bonus : How to Open a Multisell in Community Board Well i’m sure it’s easy for you now, all we need to do is to add another command to CommunityBoard since we can’t use npc_%objectId% stuff here ----------------------------------------------------------------- 2.1 Add the Command to the CommunityBoard.java You can find it in the package : net.sf.l2j.gameserver.communitybbs , Then we gonna add this code after the _bbshome code else if(command.startsWith("_bbsmultisell")) {MultisellData.getInstance().separateAndSend(command.substring(13).trim(), activeChar, null, false);} To look like : ----------------------------------------------------- 2.1 Add the Mutlisell Button to the Community Board Home Go to : data/html/CommunityBoard/top/Index.htm, and add this code any where you like it to appear : <br> <center> <button value="Multisell In Community" action="bypass _bbsmultisell 50009" width=150 height=22 back="TutorialBut.fortut" fore="TutorialBut.fortut"> </center> ------------------------------------------------------- 2.2 Now Build your Server Core and Restart Server to Test Now Guess What ?? it’s Working and we have Multisell in Community Board At the End i hope you learned something new, and i wish it’s useful to anyone Thanks and Regards 1 13
Celestine Posted October 18, 2017 Posted October 18, 2017 This is something interesting +1 keep the nice work Sam Pinned.
SamDev-Coder Posted October 18, 2017 Author Posted October 18, 2017 1 minute ago, Celestine said: This is something interesting +1 keep the nice work Sam Pinned. Thanks a lot, i hope it's useful from someone :)
Sinister Smile Posted October 18, 2017 Posted October 18, 2017 This is one of the best guide that i have ever seen in this forum. Thank you for this guide @SamDev-Coder!
SamDev-Coder Posted October 18, 2017 Author Posted October 18, 2017 2 minutes ago, Sinister Smile said: This is one of the best guide that i have ever seen in this forum. Thank you for this guide @SamDev-Coder! Thanks a lot mate for your support :)
KruMix Posted October 18, 2017 Posted October 18, 2017 Pefect guide, Sam! Keep the good work! Greetings!
SamDev-Coder Posted October 18, 2017 Author Posted October 18, 2017 9 minutes ago, KruMix said: Pefect guide, Sam! Keep the good work! Greetings! Thanks a lot brother, i'll try my best :)
Tryskell Posted October 18, 2017 Posted October 18, 2017 Just as a side note, since rev 367 the instance system naming has been edited, "L2" and "Instance" have been dropped from all instances names. The complete list edition can be found on the changeset of rev 367, when the older name has been edited (all reasons have been quoted under ()). If you got aCis dedicated guides, you can also post them on the dedicated section of Tavern section. Good job overall :).
SamDev-Coder Posted October 19, 2017 Author Posted October 19, 2017 7 hours ago, Tryskell said: Just as a side note, since rev 367 the instance system naming has been edited, "L2" and "Instance" have been dropped from all instances names. The complete list edition can be found on the changeset of rev 367, when the older name has been edited (all reasons have been quoted under ()). If you got aCis dedicated guides, you can also post them on the dedicated section of Tavern section. Good job overall :). Well sorry about that i just got the 360, and for the naming system i guess it's much better, i don't even know why someone would use L2PcInstance it's like a crypto name for me, any way i'll revise the changes list you mentioned and will try to make post updated and compatible :D And yes since this is my first content about aCis so i still got more to share :) , i'll register at this link Thanks for Support and Correction :)
Tryskell Posted October 19, 2017 Posted October 19, 2017 (edited) Well for people used to old naming convention, it's probably terrible, but yeah it's more clear reading Player rather than L2PcInstance and other strange names :). At least convention naming is respected now. Continue the good work. Edited October 19, 2017 by Tryskell
SamDev-Coder Posted October 19, 2017 Author Posted October 19, 2017 23 minutes ago, Tryskell said: Well for people used to old naming convention, it's probably terrible, but yeah it's more clear reading Player rather than L2PcInstance and other strange names :). At least convention naming is respected now. Continue the good work. I think most coders will adapt to the new naming fast since it's better. By the way i would like to ask, is any limitations regarding sharing guide/shares for aCis ?
Tryskell Posted October 19, 2017 Posted October 19, 2017 1 hour ago, SamDev-Coder said: I think most coders will adapt to the new naming fast since it's better. By the way i would like to ask, is any limitations regarding sharing guide/shares for aCis ? None, from the moment it doesn't involve share of post-free (actually 360) sources. If you reuse an existing guide and improve it, general courtesy is to link the original and reshare on original topic, if it can be replaced by a better version.
sangenegru Posted November 10, 2017 Posted November 10, 2017 On 10/18/2017 at 6:08 PM, SamDev-Coder said: Thanks a lot, i hope it's useful from someone :) It sure is, thank you!
MarGaZeaS Posted June 10, 2018 Posted June 10, 2018 good job. how can i add teleports in community board with cords? click button and go in location
Recommended Posts