Jump to content

Recommended Posts

Posted

I was researching for days, and many years ago even more time into editing ugx files, so i am sharing this tutorial for those interested. Maybe someone smarter then me will be able to finally crack the ugx file editing, like it was with interface. Credits at the bottom.

 

This guide is a work in progress as R* Scaleforms are still being digged and discovered day by day.

Ok so this is intended to build and stream your first own scaleform.
I started diggin 1 week ago into this amazing world and after an initial sense of loss, i started understanding how R* handles it.

First of all the basic tools. Youl’ll need:

 Adobe Flash CS6 PRO trial 13 (R* used CS4 and CS5 for its scaleforms but it’s ok)
 GFxExporter 16 (this is used to convert from swf to gfx format, i’ll explain later in the guide, download all the files and place them in a folder)
 JPEXS Free Flash Decompiler 7 (To decompile your scaleform… and not only that)
– FiveM Client and a Server to test with.

Part 1 - Setting everything up for work

Ok so, first of all, don’t launch Adobe Flash Pro yet, we need to create a folder where we will store all of the project first, what you call it is not important, what’s important instead is the package directory, just like how you would set it up in Java or other similar languages.

Inside your folder, create four distinct folders one inside the other, so you can get the following directory YourFolder\com\rockstargames\gtav\levelDesign\ inside this folder is where we are going to put our ActionScript files.

Now you can open Adobe Flash Pro, this is a very old application, i suggest to play with it and its options for a while… in about 1 day of playing you’ll get the hand of it :slight_smile:

Ok once opened, you can get a view like this

 

This is the Classic (base) settings of Flash’s Workspace… if you go in the Top-Right area you can change your Workspace.
I personally use the Developer one with the addition of the actions panel anchored in the Left-Bottom side of the screen as it fits perfectly what we need to do:

 

Part 2 - Creating our Project

Now that we have set our Workspace, we can create a new project, in the Left-Top part of the screen you can see the Project panel, here we can create a new project, to do that just select the drop down bar and click into "New Project…"

image

this window will appear:

image

Here you can set the name of the project for now we’ll call it GuideScaleform, then select the yellow folder icon and navigate to the “YourFolder” folder we mentioned before, the application will set the project path there.
Select the “Create default document” tick as we need it to build the graphic interface of our scaleform and give it a name (it will be your swf / gfx file name)

 Important!! Always set the Destination at Flash Player 8 and Script to ActionScript 2.0

When we’re done click “Create Project” and we have our project like this
image

The file AuthotimeSharedAssets.fla is generated automatically and can be ignored we will not use it but we need it to publish our project into swf so don’t touch it and leave it there.

now we can start working on our Scaleform.

Part 3 - The Drawing

First thing to do, is double clicking the fla file we created, an empty white page will open.
look on the right side of the screen…

 

Make sure that the flash player is version 8 and Action Script is ActionScript2, then we change our project dimensions, the fps can go from 24 to 29 to 30 in some scaleforms, up to you, the size must be 1280 x 720.
Almost all scaleforms uses 1280 x 720 screensize to work with even the 3D ones, some use smaller sizes but for now this will be good for us.

Next step is creating CONTENT and BOUNDING_BOX symbols these 2 are the main parts of the scaleform and i’ll explain later why. Go right side and click on the Add Symbol button (the very first one) or right click on the lower empty space and select “New Symbol",

 

the symbol is a Movie Clip and while it can have the name you want, it must have a linkage to the Action Script, under “ActionScript Linkage ”, check both cases and write in the Identifier box CONTENT .
Repeat the same and in the Identifier box write “BOUNDING_BOX

 

the 2 symbols should look like this
image

now, on the main screen click on that little icon spacer.png 2 on top and select whatever you called BOUNDING_BOX Symbol.

Now click on the Rectangle Tool or press R), create a rectangle, then select the "Selection Tool (or press V), select the rectangle and set its XY position to zero, the color does not matter, you can set the alpha to 0% or not as the code later will set it as invisible anyway.
This symbol represents your screen’s boundaries and will contain the custom scaleform.

Now it doesn’t need to always be 1280x720, for example if you are making a menu and want to render it in 3D or in any mode outside of Fullscreen, the rectangle should probably be the same size as the menu itself. What’s the most important from what I can guess is that the position must be zero’d.

Now using the same little icon spacer.png 2 select the CONTENT symbol to show, THIS is where we are going to draw our scaleform… everything is drawn into the CONTENT symbol.
Now Drag&Drop the BOUNDING_BOX symbol from the library into the CONTENT screen and again, set the BOUNDING_BOX rectangle’s position XY to 0.
you can see when it’s 0 because the little + symbol is on the top-left corner like in the picture

 

 

Now on the timeline in the bottom of your workspace, add a new layer to keep drawing, you can see the layers as steps, each step is a set of drawings that takes you to the end, first layer is the bounding_box’s one, the next layer will make us draw a rectangle inside the bounding box layer, place wherever you want as long as it’s inside the bounding_box layer.
you can draw your rectangle big or small as you want but not too small to let us draw some texts into it.

now adding another layer we select the Text Tool ( T ), we are going to add a text that will not change by script or user input we’ll set it as Static Text

 

In the nearby we’ll be adding a TextBox that will be filled by script via scaleform functions and natives…
Draw it and select Dynamic Text in the drop-down, leave the text box empty, and add an instance name “PlayerNameText

 

 

Since we are adding texts we need to add a font to the scaleform or they won’t be visible at all, to do that select one of the textboxes and click on the “Embed…” button located in the Properties Tab, go to the “Character ranges” category and check All in the list, next, go to the Options tab then under “Linkage” check both boxes and click OK, you should see the font added into the Library panel.

The basic drawing part is done for now.

Part 4 - The ActionScript code

Now the fun part, go up in the project folders navigate into com\rockstargames\gtav\levelDesign folder and in here click on New File icon
image
Set File Type as Action Script and call it BaseScriptUI.as ==> Create it and copy this code into it

class com.rockstargames.gtav.levelDesign.BaseScriptUI extends MovieClip
{
	var BOUNDING_BOX;
    var CONTENT;
    var TIMELINE;

	function BaseScriptUI()
	{
		super();
	}
	function INITIALISE(mc)
	{
		this.TIMELINE = mc;
		this.CONTENT = this.TIMELINE.attachMovie("CONTENT", "CONTENT", this.TIMELINE.getNextHighestDepth());
		this.BOUNDING_BOX = this.TIMELINE.attachMovie("BOUNDING_BOX", "BOUNDING_BOX", this.TIMELINE.getNextHighestDepth());
		this.BOUNDING_BOX._visible = false;
	}
}

This is the BaseScriptUI code, present in every drawn scaleform, it’s the super class for every creation you’ll see why… create a new ActionScript file in the folder and call it the same way you called your fla file

this will be your main Scaleform code and you’re going to copy this base code to start with:

class com.rockstargames.gtav.levelDesign.GUIDESCALEFORM extends com.rockstargames.gtav.levelDesign.BaseScriptUI
{
   function GUIDESCALEFORM()
   {
      super();
   }
   function INITIALISE(mc)
   {
      this.TIMELINE = mc;
      this.CONTENT = this.TIMELINE.attachMovie("CONTENT","CONTENT",this.TIMELINE.getNextHighestDepth());
   }
   function SET_PLAYER_NAME(str)
   {
      this.CONTENT.PlayerNameText.htmlText = str+" !";
   }
}

As you can see i wrote GUIDESCALEFORM all capital… since all scaleforms are wrote like this, i decided to keep it as that.
You can note that the Scaleform itself extends BaseScriptUI this is the reason why we need it and see it in almost every scaleform.
Another thing to note is that i already set a Scaleform function for you, SET_PLAYER_NAME is the function you’ll call to set the empty text box we drew earlier… for convention all the “public” scaleform functions (the ones that can get called by the scripts) are written ALL_CAPITAL_WITH_UNDERSCORES while internal functions are always written lowCapitalWithoutUnderscores

The last part of the work is basically adding the actions and publish our scaleform.
Click on your scene button
image

If you don’t have the Action panel as i do, simply press F9 and anchor the window to one of the side panels.

In the Action window we’re going to paste this code,

var TIMELINE = new com.rockstargames.gtav.levelDesign.GUIDESCALEFORM(); // Constructor of your custom scaleform
TIMELINE.INITIALISE(this);

This is the last code to be passed when publishing, if there are errors anywhere in your ActionScript codes or in the drawing, the swf will be still published but this timeline won’t be, if decompiling your scaleform you can’t find this code, this means you have an error somewhere in your code.
When writing code is good practice to always check for errors and to auto-indent by using these 2 symbols in the editor, if an error appears it will be shown.
image

Part 5 - Publishing

Go to “File” on top and select “Publish Settings”, be sure that the Target is set to Flash Player 8.0 and Script to ActionScript 2.0, then click OK.

Go to “File” again and select “ActionScript Settings”, then set a Classpath to your com folder inside your project folder and click OK.

Now we are ready, go to “File” and select “Publish” (or use Alt + Shift + F12) and in your project folder you’ll see a new “GuideScaleform.swf” file.

Now this is a good time to check if everything is set up properly, if you see something missing, go back and see if you missed something.
To be sure everything is gone as expected, you can also open the swf file with JPEXS Free Flash Decompiler and you’ll see that everything we’ve done is there, check for the timeline too in the “Script => frame 1 => DoAction” file… in the end it should be something like this

 

Finally, open the folder where gfxexport is located, drag the .SFW file onto gfxexport.exe, wait a sec and it should create a .GFX file in our project directory near the .SWF file.

Now here’s the hardest part, be very attentive, take this .GFX file and put into your stream folder.
And congratulations! You just made and installed a custom scaleform in your server! Now all you have to do is call it like any other scaleform by using the name of the file with either your own method or the usual Natives.

Part 6 - What’s next

Digging and trying… i’ve found out you can actually use internal R* tools to better create your code and scaleform, for example, if you add a png file to the scaleform like this:

 

GFxExporter will strip the images and leave only red shapes (yeah the same you saw into other scaleforms) the trick is here… the exporter will take the stripped pics and turn them into .DDS files with weird naming, if you put those .DDS files into a custom .YTD file and place it near your .GFX file with the same name and bam… the scaleform will load the shapes automatically

 

If you still need to load some textures via code, then you can do it like this, there are 2 ways:

  1. Load the texture file via script (RequestStreamedTextureDict()) and then use this code to draw the texture onto a custom symbol in the CONTENT timeline
	function loadTextureIntoMovieClip(txd, texture)
	{
		var thisObj = this;
		var _loc3_ = this._componentsForLoadingImages.length + 1;
		var _loc2_ = new com.rockstargames.ui.core.BaseComponentInfo(_loc3_);
		this._componentsForLoadingImages[_loc3_] = _loc3_;
		_loc2_._depth = _loc3_ + 10;
		_loc2_._mc = this.CONTENT.yourCustomItem; // this is the symbol you created in content like a rectangle turned to symbol
		_loc2_._listener = new Object();
		_loc2_._loader.addListener(this.loaderObject._listener);
		_loc2_._listener.thisObj = thisObj;
		_loc2_._listener.componentID = _loc3_;
		_loc2_._listener.onLoadInit = function(target_mc)
		{
			var _loc2_ = this.thisObj._componentsForLoadingImages[this.componentID];
			_loc2_._loader.removeListener(_loc2_._listener);
			_loc2_._loader = null;
		};
		var _loc5_ = "img://" + txd + "/" + texture;
		_loc2_._loader.loadClip(_loc5_,_loc2_._mc);
	}
  1. Let the game do the work for you without natives using internal calls:
    com.rockstargames.ui.game.GameInterface.call(“REQUEST_TXD_AND_ADD_REF”) this will request the dictionary without using the scripts natives. Its parameters:
1. type (int) -> for generic textures use com.rockstargames.ui.game.GameInterface.GENERIC_TYPE
2. gfx file name (string) -> this is not the name of the scaleform.. but the gfx file.. if you change name to the file.. change name here!
3. txd (string) -> the dictionary of the texture to load
4. (optional) type of texture i think (string) -> it's a string that change in base of needs.. sometimes it's background for backgrounds.. for callscreen is "callscreen_thumbnail", optional in some gfxs
5. alreadyLoaded (bool) -> i think this is a way to check if the texture dict was loaded already.. dunno but it's optional in some gfxs

callback parameters ADD_TXD_REF_RESPONSE:

1. textureDict (string) -> the texture dict requested
2. success (bool) -> true if the request was succesful
3. uniqueId (int) -> not used.. sometimes this and success are inverted ADD_TXD_REF_RESPONSE(textureDict, success, uniqueID) -> ADD_TXD_REF_RESPONSE(textureDict, uniqueID, success)

The callback is called automatically and MUST be included into your scaleform code or else it won’t work… an example:

	function requestTexture(txd)
	{
		if (txd != "" && txd != undefined)
		{
com.rockstargames.ui.game.GameInterface.call("REQUEST_TXD_AND_ADD_REF",com.rockstargames.ui.game.GameInterface.GENERIC_TYPE,"TEST_7",txd);
		}
	}

	function TXD_HAS_LOADED(textureDict, success)
	{
		if (success)
		{
			if (textureDict == this._stockMenuDict)
			{
				this.loadTexture(textureDict,this._bannerTexture,this.bannerSprite);
				this.loadTexture(textureDict,this._bodyTexture,this.bodySprite);
			}
		}
	}

	function loadTexture(txd, texture, container)
	{
		var thisObj = this;
		this.loaderObject._depth = 100;
		this.loaderObject._mc = container;
		this.loaderObject._loader = new MovieClipLoader();
		this.loaderObject._height = this.loaderObject._mc._height;
		this.loaderObject._width = this.loaderObject._mc._width;
		this.loaderObject._x = this.loaderObject._mc._x;
		this.loaderObject._y = this.loaderObject._mc._y;
		this.loaderObject._listener = new Object();
		this.loaderObject._loader.addListener(this.loaderObject._listener);
		this.loaderObject._listener.thisObj = thisObj;
		this.loaderObject._listener.onLoadInit = function(target_mc)
		{
			target_mc._width = this.thisObj.loaderObject._width;
			target_mc._height = this.thisObj.loaderObject._height;
			target_mc._x = this.thisObj.loaderObject._x;
			target_mc._y = this.thisObj.loaderObject._y;
			var _loc2_ = this.thisObj.loaderObject;
			_loc2_._loader.removeListener(_loc2_._listener);
			_loc2_._loader = null;

		};
		var _loc3_ = "img://" + txd + "/" + texture;
		this.loaderObject._loader.loadClip(_loc3_,this.loaderObject._mc);
	}

 

Conclusion

credits: a HUGE thank you to @lacoL for helping me into the digging and learning Flash :smiley:

 

  • Like 4
Posted

I also can extract tga from ugx , maybe even repack them but i don t have time, if anyone interested i can share the tools (they are very old, hard to find). But i cannot repack them, but i think it s because of my limitation.

It should not be very difficult, for someone who knows coding i think, but maybe i m wrong. 

  • 11 months later...
  • 5 months later...
Posted (edited)

i just wish to thank you for sharing my Scaleform Guide 😄 i invite you all to join my discord guild where we talk about coding and scaleforms 😄 also.. no credits to the creator bro?? Really?? https://forum.cfx.re/t/how-to-tutorial-create-your-own-scaleform-using-flash-gfxexporter/4763644 this is where the guide was stolen 🙂 Happy to have helped someone creating scaleforms for GTA!
Here the discord invite 
KKN7kRT2vM

Edited by manups4e

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Posts

    • Introducing: Containers to Roll   Players now have the ability to win containers/cases via the Roll System. Additionally I also added a global leaderboard displaying the users with the most roll games. This can be disabled/enabled via Admin Management Panel. Also improved the winning display with a volumetric Godrays effect.  
    • I search job: posting your advertisement(sale,service) on various forums. Contacts for communication. You can find link for download messenger using Google search.   Telegram https://t.me/negotiato_r @negotiato_r   Element(based in United Kingdom) You can find me using this name. @negotiato-r:matrix.org   Session(based in Switzerland) You can find me using this name. 05770c2eda571fc8d10ec0e79e258ec0d9189def2a3e1f2ace1cd29a2174d40723   Delta Chat(based in Germany) You can find me using the link below. https://i.delta.chat/#1ABEBFFCBC1AEE629111387073FFDA1835BB423E&i=6WtJxcgJGcFD3vIpglQfhe5J&s=f2EkRsqxAeFYep9g9s1y1aIf&a=xuozjaudg%40nine.testrun.org&n=negotiator   I ask administrator or moderator not to consider this link an advertisement for messenger.  This is only link that people can use to contact me.  There is also QR code option,but you have to use mobile phone to access QR code.  This means you have to install VPN app on your mobile phone,then sync your account from your mobile phone to your laptop or computer.  This is a very cumbersome process.  It's much easier to use pre-made link for laptop or computer. Hello. I intermediary. I search job: posting your advertisement(sale,service) on various forums.  My service is free: posting your advertisement(sale,service) on various forums. I know these forum addresses,i can post your ad(for sale,service) on various forums. Dear sellers and those who provide any services. I offer you cooperation. My commission is not taken from your amount,my commission is added to your amount. From money received from guarantor,you pay me my commission.  Payment is made on Tether USDT TRC20 or on Tron TRX. Commission for sending from your wallet to my wallet paid by buyer. When communicating via messenger,please tell me what your commission is for sending on Tether USDT TRC20 or on Tron TRX.  Amount(fees) you'll pay as shipping fee to my wallet will be added to total amount. Payment will be made by guarantor to your payment details. Buyer deposits total amount with my percentage. Send me in messenger your ad copy with price(s). Independently from that through which messenger will be communication,buyer suggests using forum guarantor,gives forum address(http address) and send link(http address) to me,link i will pass on to you(seller) for consideration. If you as seller are not satisfied garant service on proposed forum,i say buyer goodbye and he goes to look for his product(service) from someone else,as result i will wait new buyer.   If sale amount is less than $1000,i receive 20 percent above your total amount. If sale amount is more than $1000,i receive 10 percent above your total amount. I do not deal with either buyers or sellers from Ukraine(i do not cooperate with this country). I will not accept any advertising related to Ukraine,as i do not cooperate with this country. For buyers from other countries guarantor's services are entirely at buyer's expense. You can offer me any other area cooperation that does not violate law.  I do not give 100% guarantee that i will accept your offer,which is not initially related to my advertising area.  It is 50/50 that i will either refuse you or accept your offer.  Everything will depend on whether this offer does not violate law.  I will read information about your product(service) in Google search engine that you offer me for advertising and make decision,which i will inform you in messenger for communication.  I will need some time to familiarize myself with information from Google search engine. I'm currently interested in 4 areas: 1)promotional offers with discounts only(coupons or promo codes):food,shoes,clothing,furniture,cosmetics,household appliances,consumer electronics,taxis,bus tickets,train tickets,plane tickets,hotel tickets,gas coupons or promo codes for car owners I do not advertise Ukraine,do not cooperate with it and have no dealings with it. I will not advertise anything related to carding.  Buyer deposits amount for product(service) plus my commission(20 percent based on amount for product or service) into guarantor and then receives their product(service) in forum transaction.  I would be grateful if it were possible for buyer to receive their goods somehow after depositing money with guarantor,without return address or contact information for future purchases. It's not in my best interests for buyer to communicate directly with you after first purchase. If this isn't possible,then you will simply agree with buyer to receive money with my percentage higher than your initial payment each time. If same customer purchases from you second time,customer pay you together with my percentage and i receive this percentage from you,this will provide additional incentive to advertise,i will promoting you on other forums.     2)selling real estate(houses or apartments) I'm not interested renting. I'm willing to advertise all countries except Russia and Ukraine.  I won't advertise these two countries. I don't advertise Ukraine,don't cooperate with it and have no dealings with it. I'm not interested house or apartment listings that appear on Google search pages,as buyer can find information there themselves without my help and buy house or apartment in desired country. I'm interested house or apartment that aren't listed on Google search. How i see this ad:buyer sees my listing for desired country and if they're interested,they deposit 10 percent listed price for house or apartment in Garant Service. Buyer sets  deadline in forum transaction,during which i either receive my money or don't.  Then buyer receive an address,day and time to meet with seller. Buyer takes lawyer and notary with them and flies(or is driving car) to  given address. If purchase transaction falls through,buyer collects their percentage from guarantor. I don't think buyer willing to buy  house or apartment worth more than 12545$ is willing to cheat me out  that 10 percent by making up  fake story about  failed deal.       3)selling telegram premium status Buyer has two options: 1) transaction through guarantor 2) transaction without guarantor   If transaction is through guarantor. I(intermediary) conduct transaction with guarantor. Buyer specifies following terms in terms transaction: 1) i authorize the disclosure of the transaction name to third parties(that is to you) 2) i authorize the disclosure of the seller's payment details(your payment details) to third parties(that is to you) 3) i authorize the disclosure of the total transaction amount to third parties(that is to you) 4) i do not authorize the disclosure of my profile link on this forum to third parties 5) i do not authorize the disclosure of my contact information(if i have any in my profile on this forum) to third parties   If activating premium status requires logging into buyer's account,i will do this.  You will provide me with instructions on how to activate premium status for buyer's account. If you want to contact me about selling premium status on telegram, but my telegram account is unavailable(account is frozen or telegram system has deleted it),you can contact me using my other contact information. To activate premium status by logging into buyer's account,i will download portable version telegram from official website and launch it on my laptop.  I will enter mobile phone number buyer provides me in messenger they originally contacted me through and send login code to this number.  Buyer will then send me login code. Once transaction is finalized and buyer has deposited funds into guarantor's account I'll notify you via messenger. You register on  forum suggested by buyer.  Message guarantor privately on forum,asking them to share all points I've outlined above.  Buyer will provide  link to guarantor's forum profile in advance or you can find guarantor's forum profile on forum yourself,it's up to you to decide. After verifying that your payment details are included and that transaction amount matches amount agreed upon in messenger, you upgrade buyer to premium status. Your payment details are specified in application,in formquestionnaire for forum transaction,but you won't receive money from guarantor until buyer will not receive service(product),as soon as buyer receives service from you,guarantor will pay you. If buyer has received premium status,you receive funds from guarantor and then pay me my commission using my payment details. The fee for sending from your wallet to my wallet is covered by buyer,not you. When communicating via messenger please tell me your fee for sending to Tether USDT TRC20 or Tron TRX. Buyer deposits funds into guarantor with total amount already including my percentage plus buyer's fee for sending,which you will spend by paying me my percentage when transferring from Tether USDT TRC20 or Tron TRX. If transaction is without guarantor. Buyer pays money to your payment details received from me via messenger and waits for service to be rendered. I will inform buyer total amount when communicating via messenger. You upgrade buyer to premium status through me and then you pay me my percentage to my payment details.  If activating premium status requires logging into buyer's account. I will do so.  You will provide me with instructions on how to activate premium status for buyer's account. Fee for sending from your wallet to my wallet is covered by buyer,not you.  When communicating via messenger please tell me your fee for sending to Tether(USDT TRC20) or Tron(TRX). Buyer pays you total amount,including my percentage plus buyer's fee for sending,which you will spend by paying me my percentage when transferring from Tether USDT TRC20 or Tron TRX.       4)i offer cooperation to specialists who provide services for collecting and submitting documents to consulate for citizenship,residence permits,visas and schengen visas I will advertise service collecting and sending documents to consulate only for following countries:Commonwealth of Independent States,Europe,Mexico,United states america,Canada,United Kingdom,Asia,Africa. Russia and Ukraine:these two countries i will not advertise. Buyer pays guarantor(amount from seller) for service for collecting and sending documents to consulate plus my commission(10 or 20 percent based on service fee). Buyer sets deadline in forum transaction within which they must receive service. Then in forum transaction buyer wait provision service. If after specified period(which will be specified in transaction),consulate refuses client's service,you as specialist have right to charge exact amount for your work through guarantor,since you spent your time on it(this clause will be specified in transaction). What will be amount you will decide,send solution through me.I'll let the buyer know. Client does not pay my percentage if consulate refuses client's service(this clause will be specified in transaction).  In case refusal to buyer from consulate you will need to confirm this refusal through website. Whenever you collect and submit documents on country's website,request is created through their website.  You will provide access to this request to guarantor.  This is necessary to ensure that buyer doesn't pay for nothing,meaning amount you will be required to receive through  guarantor for service provided if  consulate's request is unsuccessful.
    • Hey MaxCheaters! 👋 Introducing L2Soon.com — a free international platform for Lineage 2 server announcements.   Why L2Soon? No more searching through dozens of forums and Discord servers. All new L2 server openings are in one place — updated daily, with real player online counts so you always know where people actually play.   Features: 🔔 Telegram Bot (@l2Soon_bot) — alerts 24h & 1h before server launch 📅 Accurate launch times — in your local timezone ⚔️ All chronicles — Interlude, High Five, GoD, Classic, Essence, Grand Crusade and more 🎯 Filters — by chronicle, rates (x1–x1000+) and server type (PvP, RvR, GvE, Craft, Low Rate...) ⭐ VIP servers — verified projects pinned at the top 🌍 Multi-language — EN, UK, RU, PT   Listing is completely FREE. 🔗 https://l2soon.com/en Feedback welcome — drop a comment or contact us via Telegram @l2Soon_bot
  • Topics

×
×
  • Create New...

Important Information

This community uses essential cookies to function properly. Non-essential cookies and third-party services are used only with your consent. Read our Privacy Policy and 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..