Jump to content

KeyWorld

Members
  • Posts

    379
  • Joined

  • Last visited

  • Days Won

    11

Posts posted by KeyWorld

  1. Some news today.

     

    Map loading

    I really improve my map loading : update the zlib unpacker, optimize the mesh compilation.

    It's now about 2-3 times faster than before. It takes ~3 seconds to load a map like Izlude or Prontera, instant loading for little map. Lighthalzen seems to be the slowest to load : ~5 seconds.

    I'm quite happy of the result.

    Sound objects

    I added sound objects to the map, I'm not certain of the volume to use for this objects (Audio API allow a min volume of 0.0 and max volume of 1.0; there are some sound object with a volume of 2.0 so not sure yet what the max volume is here).

    You can see the sound visualization of aldebaran (all porings are a sound object):

    aldebaran-sound.jpg

     

    Lub files

    Since Renewal, a big part of servers use lub files to store data info, so it should be normal for robrowser to load this files at runtime to build its DB.

    So I wrote a Lub decompiler (PHP, and converted the code to JS), it don't support all opcode but is enough to extract tables (I don't care of extracting functions).

    Here the PHP code in action: http://www.robrowser.com/prototype/lub/

    For now I'm only able to extract lub version 0x51 but it seems some client use the 0x50 version (why ? :(), so I will have to extend the code later.

     

    Sprite Rendering

    I re-wrote the whole Entity Manager code, as the old one it support all actions/animations.

    I added some setter to update automatically the sprites/act/palettes when modifying/setting a new visual look, ex: you change the sex, it will automaticaly reload all sprites/actions (head, hat, weapon, ...).

    Palettes are applying on the pixel shader to avoid having to re-build each sprite image (faster and easier).

    I also face the same problem as Shinryo in his topic and used the same way to fix it, but it's definitively not the way to go : collision with roof, weird result when sprite is on the water.

    The better way is to project the top vertices to the up vector of the bottom part and modifying UVs to match the new form in the screen, but it's more complicate and I lack knowledge on 3D maths.

     

    Weather

    I just add sky and clouds to some maps (yuno, gonryun, thana_boss, 5@tower, ...) using a config file in the DB, it's funny how the client manage it. The result looks good:

    ra_temsky.jpg

     

     

    Other news

    • IE11 will support webgl
    • Web API Device Storage will allow to use local GRF without needed to select it each time at runtime (I hope this standard will be accept...).
    • Firefox added ASM.js in the latest nighty build. It allow to run some js (at this time just arithmetic code) script at 2 times slower than native speed (comparable as Java and C#), by doing some low typage on variables.
    • Upvote 5
  2. It will be released this year even if it's not totally finished, it will be a good thing to open the source :)

    Same here, I try to do my best to work on this project but sometimes it's hard to manage it and all others things (like my girlfriend lol).

    It can be a good thing to have a GRF history/diff/tracking tool to see what Gravity add/remove/change in its files, keep it up ! (I know some others guys had the same idea :P)

    Nice js1k entry, I asked myself "why 'ninja' ?" the first time I saw it, nice done :)

    @project,

    No news, have problems with one of my computer, so I will try all I did in "bind mode" (rewrote the whole Entity manager) when I will switch to my girlfriend's home this week end :)

    • Upvote 5
  3. purchase a SSL certificate and make your server cp secured. plus, seeing a https:// in your website wil make your players feel secured too and wil have confidence in you for keeping their data secured.

    Feel secured doesn't mean be secured.

    Https don't protect from SQL injection and some hacks methods.

    Surprisingly the hacker only know how to delete the char table, and luckily i got perform daily backup.

    Maybe because he doesn't want to cheat but just want to annoyed your server.

    As everyone said, it can come from any tool you add to your CP (forum, control panel, bug tracker) or in game npc (check for query_sql) or even from the hoster or your own team.

    • Upvote 1
  4. Yeah I rewrote the whole core as I said before. The old one wasn't really well optimized and organized, and pretty hard to update, now it's much better and user-friendly :D

    About your way to do, it missing some structure at my point of view and will be maybe hard to maintain in future (and it seems you generate the password hash even if not used), but yeah it's fun the way you did it, to merge packet and just define the header to build the correct one.

    Hmm maybe I will remove the packet_id from the Network.bindPacket() and move it to the packet structure instead, I don't think we have to care about it at this step...

    About js1k yeah it was fun to do lol but very hard to compact to 1024bytes, but I'm really satisfied about the result.

    Nice to see you here curiosity :)

    What about your project ?

    @Project

    Now support 0x200 GRF version with DES encryption.

  5. I just add the network core in roBrowser.

    I search a way to make it organized and user-friendly to use.

    So basically, all packets are converted from http://rathena.org/wiki/index.php?title=Packets using tools/packet-builder/.

    Example to parse:

    Network.bindPacket( 0x69, PACKET.AC.ACCEPT_LOGIN, function(data){
        // data.AuthCode, data.AID, data.userLevel, data.ServerList, ...
        // do whatever you want with "data"
    });
    Example to send:
    var pkt = new PACKET.CA.LOGIN();
    pkt.ID = "Test";
    pkt.Passwd = "Test";
    pkt.Version = 20;
    Network.sendPacket(pkt);
    If you have a better idea, let's talk about it :)

    There will be three differents ways to connect to server:

    • Using JAVA.
    • Using Chrome Socket API.
    • Using WebSocket to connect to a Proxy server.
    For moment I am focus on JAVA.
    • Upvote 1
  6. wow nice Default Theme.

    Thank you :D

     

     

    it has adding item in webshop?

    how to use it :)

    As I said in the main post, it's a design integrated to an index.html page. There is no CP in the archive, you are free to integrate it yourself to your favorite one.
    • Upvote 1
  7. File Name: CeresCP Default Theme

    File Submitter: KeyWorld

    File Submitted: 26 Feb 2013

    File Category: Web Resources

    Content Author: KeyWorld

    If you remember this topic there was a contest for the upcoming Cerescp default theme.

    For now, I don't know if developers stopped to work on the new CeresCP version, but a lot of time passed since the last update/announce and some members asked me to release the default theme I made.

    I hope the best to CeresCP developers, the theme can always be used if they continue the project.

    cerescp_thumb.png

    So in this archive, an index.html page with css/javascript and images.

    Compatible (the last time I checked) with:

    • IE6, IE7, IE8, IE9, IE10
    • Safari
    • Chrome
    • Opera
    • Firefox
    You can check the online version: http://upload.robrowser.com/templates/ceres/

    There is no CP in the archive, you will have to integrate it to your favorite one yourself (hey don't pay someone to do it, all the work is already done seriously...).

    Have fun :)

    Click here to download this file

    • Upvote 5
  8. You can find it by reversing the client, or by searching in some source tools as Actor.

    But in the past we just have reverse engineering and trying to search by finding redundant informations in the hex.

    But your current function is not really good, some bugs on some versions, not really good name convention...

    If you want mine:

    <?php
    
    /**
    * @fileoverview Action - Loader for the Gravity .act file
    * @author Vincent Thibault (alias KeyWorld - Twitter: @robrowser)
    * @version 2.3.0
    */
    
    class Action
    {
    
    	private $fp, $version;
    	public $actions = array(), $sounds  = array();
    
    
    
    	/// Loader on construct
    	public function __construct($filename=false)
    	{
    		if ( $filename )
    			$this->open($filename);
    	}
    
    
    
    
    	/// Open a Action file
    	/// Do some check before.
    	public function open($filename)
    	{
    		if ( substr( $filename, 0, 7 ) !== "data://" )
    		{
    			if ( ! file_exists($filename) )
    				throw new Exception("ACT::open() - Can't find file '{$filename}'.");
    	
    			if ( ! is_readable($filename) )
    				throw new Exception("ACT::open() - Can't read file '{$filename}'.");
    
    			$this->size = filesize($filename);
    	
    			if ( $this->size < 0x08 )
    				throw new Exception("ACT::open() - Incorrect file size, shoulnot be a ACT file");
    		}
    
    		$this->fp = fopen($filename,'r');
    		$this->load();
    	}
    
    
    
    	
    
    	/// Load an Action file
    	private function load()
    	{
    		extract( unpack( "a2head/C2ver", fread($this->fp, 0x4 ) ) );
    
    		if ( $head !== 'AC' )
    			throw new Exception("ACT::load() - Incorrect act header, is '{$head}' - should be 'AC'");
    
    		$this->version = $ver1/10 + $ver2;
    		$this->readActions();
    
    
    		if ( $this->version >= 2.1 )
    		{
    			// Sound
    			extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
    			for ( $i=0; $i<$count; ++$i )
    				$this->sounds[$i] = current( unpack('a40', fread( $this->fp, 40 ) ) );
    
    			// Delay
    			if ( $this->version >= 2.2 )
    				foreach( $this->actions as &$action )
    					$action->delay = current( unpack("f", fread($this->fp, 0x04) ) ) * 25;
    		}
    	}
    
    
    
    	/// Load ACT actions
    	private function readActions()
    	{
    		extract( unpack("vcount/x10", fread( $this->fp, 12 ) ) );
    		for ( $i=0; $i<$count; ++$i )
    		{
    			$this->actions[$i] = (object) array(
    				"animations" => $this->readAnimations(),
    				"delay"      => 150
    			);
    		}
    	}
    
    
    
    
    	/// Load ACT animations
    	private function readAnimations()
    	{
    		extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
    		$animations = array();
    	
    		for ( $i=0; $i<$count; ++$i )
    		{
    			fseek( $this->fp, 32, SEEK_CUR );
    			$animations[$i] = $this->readLayers();
    		}
    	
    		return $animations;
    	}
    
    
    
    	/// Load ACT layers
    	private function readLayers()
    	{
    		extract( unpack("Vcount", fread( $this->fp, 0x04 ) ) );
    		$layers = array();
    
    
    		if ( $this->version < 2.0 )
    		{
    			$size   = 0;
    			$struct = "";
    		}
    		else if ( $this->version < 2.4 )
    		{
    			$size   = 16;
    			$struct = "C4color/f1scale/Vangle/Vspr_type";
    		}
    
    		else if ( $this->version === 2.4 )
    		{
    			$size   = 20;
    			$struct = "C4color/f2scale/Vangle/Vspr_type";
    		}
    	
    		else
    		{
    			$size   = 28;
    			$struct = "C4color/f2scale/Vangle/Vspr_type/Vwidth/Vheight";
    		}
    
    
    		for ( $i=0; $i<$count; ++$i )
    		{
    			$param      = unpack( "Vx/Vy/Vindex/Vis_mirror/" . $struct, fread($this->fp, $size + 16) );
    			$layers[$i] = (object) array(
    				"pos"       => array( $param['x'], $param['y'] ),
    				"index"     => $param['index'],
    				"is_mirror" => $param['is_mirror'],
    				"scale"     => array( 1.0, 1.0 ),
    				"color"     => array( 255, 255, 255, 255 ),
    				"angle"     => 0,
    				"spr_type"  => 0,
    				"width"     => 0,
    				"height"    => 0
    			);
    			$layer      = &$layers[$i];
    
    			$layer->color[0] = $param['color1']/255;
    			$layer->color[1] = $param['color2']/255;
    			$layer->color[2] = $param['color3']/255;
    			$layer->color[3] = $param['color4']/255;
    			$layer->scale[0] = isset($param['scale1']) ? $param['scale1'] : 1.0;
    			$layer->scale[1] = isset($param['scale2']) ? $param['scale2'] : $layer->scale[0];
    
    			if ( isset($param['angle']) )    $layer->angle    = $param['angle'];
    			if ( isset($param['spr_type']) ) $layer->spr_type = $param['spr_type'];
    			if ( isset($param['width']) )    $layer->width    = $param['width'];
    			if ( isset($param['height']) )   $layer->height   = $param['height'];
    		}
    
    
    		$sound = -1;
    		if ( $this->version >= 2.0 )
    			extract( unpack("Vsound", fread($this->fp,0x04) ) );
    
    
    
    		$pos= array();
    	
    		if ( $this->version >= 2.3 )
    		{
    			extract( unpack("Vcount", fread($this->fp,0x04) ) );
    			for ( $i=0; $i<$count; ++$i ) {
    				$pos[$i] = (object) unpack( "Vunk/Vx/Vy/Vattr", fread($this->fp,16) );
    			}
    		}
    	
    		return (object) array(
    			"layers"    => $layers,
    			"sound"     => $sound,
    			"pos"       => $pos	
    		);
    	}
    
    
    
    	/// Compile a ACT file
    	public function compile()
    	{
    
    		// Header
    		$result  = pack('a2C2', 'AC', $this->version * 10 % 10, floor($this->version * 10)/10 );
    
    		// Action count
    		$result .= pack( 'vx10', count($this->actions) );
    
    		// Compile each actions
    		foreach( $this->actions as $action )
    		{
    
    			// Number of animations
    			$result .= pack( 'V', count($action->animations) );
    
    			// Compile animations
    			foreach( $action->animations as $animation )
    			{
    
    				// 32 uknown offset + layers count
    				$result .= pack( 'x32V', count($animation->layers) );
    
    				// Compile layers
    				foreach( $animation->layers as $layer )
    				{
    					$result .= pack('V4', $layer->pos[0], $layer->pos[1], $layer->index, $layer->is_mirror);
    
    					if ( $this->version < 2.0 )
    						continue;
    
    					else if ( $this->version < 2.4 )
    						$result .= pack(
    							'C4fV2',
    							$layer->color[0]*255,
    							$layer->color[1]*255,
    							$layer->color[2]*255,
    							$layer->color[3]*255,
    							$layer->scale[0],
    							$layer->angle,
    							$layer->spr_type
    						);
    
    					else if ( $this->version === 2.4 )
    						$result .= pack(
    							'C4f2V2',
    							$layer->color[0]*255,
    							$layer->color[1]*255,
    							$layer->color[2]*255,
    							$layer->color[3]*255,
    							$layer->scale[0],
    							$layer->scale[1],
    							$layer->angle,
    							$layer->spr_type
    						);
    
    					else 
    						$result .= pack(
    							'C4f2V4',
    							$layer->color[0]*255, 
    							$layer->color[1]*255,
    							$layer->color[2]*255,
    							$layer->color[3]*255,
    							$layer->scale[0],
    							$layer->scale[1],
    							$layer->angle,
    							$layer->spr_type,
    							$layer->width,
    							$layer->height
    						);
    				}
    
    
    				// Animation sound
    				if ( $this->version >= 2.0 )
    				{
    					$result .= pack('V', $animation->sound );
    				}
    
    				// Animation imf (head pos)
    				if ( $this->version >= 2.3 )
    				{
    					$result .= pack('V', count($animation->pos) );
    					foreach( $animation->pos as $pos )
    					{
    						$result .=  pack( 'V4', $pos->unk, $pos->x, $pos->y, $pos->attr );
    					}
    				}
    			}
    		}
    
    		
    		if ( $this->version >= 2.1 )
    		{
    			// Comple sounds
    			$result .= pack( 'V', count($this->sounds) );
    			foreach( $this->sounds as $sound )
    			{
    				$result .= pack( 'a40', $sound );
    			}
    
    			// Comple delay
    			if ( $this->version >= 2.2 )
    			{
    				foreach( $this->actions as $action )
    				{
    					$result .= pack( 'f', $action->delay / 25 );
    				}
    			}
    		}
    
    		return $result;
    	}
    }
    
    ?>
    Maybe should I release my character-viewer...

    Have fun~

  9. Well test this:

    	public function readAct($act,$save,$output,$code) {

    // Open the act file.

    $this->act = fopen($act, "rb");

    // Just like on the sprites we read some infos: ident=ACT, version, number of frames

    $this->aHeader = unpack('H4ident/Sversion/SnumFrames', fread($this->act, 0x06));

    // Get the filesize (used this for sql database etc.)

    $this->aSize = filesize($act);

    // The total amount of animations used

    $this->aAnimations = $this->aHeader['numFrames'];

    // The number of actions is the num of animations divided by 8.

    // So what are the "actions"? I just named them like this. They're basicly the directions you can look at. Just like in actOR.

    // \ | / }

    // -- o -- }-> 8 in total

    // / | \ }

    $this->aActions = ($this->aAnimations / 8);

    // The version

    $this->aVersion = dechex($this->aHeader['version']);

    // Format the version nicely

    $this->aVersion = (double)preg_replace('{([0-9])(.*?)([0-9])}', '$1.$2', $this->aVersion);

    // Skip some useless data on one needs

    fseek($this->act, 0xA, SEEK_CUR);

    // We're currently at action 0

    $currentAction = 0;

    // Our index for the while loop

    $anim = 0;

    // Arrays we save our informations in

    $pointerMemoryANIM = array();

    $pointerMemoryNF = array();

    $pointerMemoryPAT = array();

    // Lets go through the animations, shall we?

    while($anim < $this->aAnimations) {

    // First how many Frames does the action have?

    // On a normal headgear this would be 3. Because when you are sitting downwards you can look down with your head, left and right. So 3 in total.

    // Ff you have an animated sprite its more obviously

    $this->aAnimData[$anim] = unpack('lnumFrames', fread($this->act, 0x04));

    // So lets loop through those Frames, cuz they all got individual informations for us

    for($nf = 0; $nf < $this->aAnimData[$anim]['numFrames']; $nf++) {

    // Skip useless data

    fseek($this->act, 0x20, SEEK_CUR);

    // And now we check how many subFrames this frame got. Or better said Patterns (like in actOR)

    // For example, if you have a monster sprite and some special effects as seperates sprite frames in the .spr file.

    // You'll have to paste them onto the stage right? Thats where the patters are being created. Like layers in photoshop.

    $this->aAnimData[$anim][$nf] = unpack('lnumSubFrames', fread($this->act, 0x04));

    // Loop through the patterns or layers for more info about them

    for($patNo = 0; $patNo < $this->aAnimData[$anim][$nf]['numSubFrames']; $patNo++) {

    $this->aAnimData[$anim][$nf][$patNo] = unpack('lxOffset/lyOffset/lsprNo/lmirrored/Cred/Cgreen/Cblue/Calpha', fread($this->act, 0x14));

    if($this->aVersion < 2.0 );

    else if($this->aVersion < 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxyScale/lrotation/lsprType', fread($this->act, 12));

    else if($this->aVersion == 2.4 ) $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lspr_type', fread($this->act, 16));

    else $this->aAnimData[$anim][$nf][$patNo] += unpack('fxScale/fyScale/lrotation/lsprType/lsprWidth/lsprHeight', fread($this->act, 24));

    }

    // The end of the Patterns/Layers on the Frame.

    // So lets see if it has some kind of sound attached to it? (To the frame)

    fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

    extract( unpack("lnumExtraInfo", fread($this->act,0x04) ) );

    $extraInfos = array();

    for ( $i=0; $i<$numExtraInfo; ++$i ) {

    $extraInfos[$i] = unpack( "x4/VExtXOffsetX/VExtXOffsetY/x4", fread($this->act,16) );

    }

    }

    // Increase our Action and loop through the next frames and patterns^^

    $anim++;

    }

    // After reading all infos we are at the end of the act file where the sound informations are stored.

    $this->aSoundData = unpack('lnumSounds', fread($this->act, 0x04));

    // It has some sounds?

    if($this->aSoundData['numSounds'] > 0) {

    // We loop through number of sounds to grab it.

    for($nSnd = 0; $nSnd < $this->aSoundData['numSounds']; $nSnd++) {

    // Now because the sound file is a string (directs to some .wav file in the grf), saved in hex and for our advantage always exactly 40 bytes long

    // we read the string as hex values and save it like that

    $this->aSoundData[$nSnd] = unpack('H80file', fread($this->act, 0x28));

    }

    }

    // Oh we also got the interval a frame is played at? That info is ours! >:D

    for($intv = 0; $intv < $this->aAnimations; $intv++) {

    $this->aAnimData[$intv] += unpack('fanimSpeed', fread($this->act, 0x04));

    }

    // Lets format our earlier saved hex string to a actual string with letters and not just numbers ^^

    // Some note: If there are sounds in the act (mostly monsters) there always appears to be a sound called "atk", just like that.

    // It doesnt exist in the grf and I have no idea how it is being handled. Maybe some description of the sounds?

    foreach($this->aSoundData as $id=>$string) {

    if((string)$id != 'numSounds')

    $this->aSoundData[$id] = str_replace(pack('H2','00'),"",(pack('H*', $string['file'])));

    }

    // Lets display our results of the act file.

    $act = '$jact';

    if (strpos($output, "head")) {

    $act = '$hact';

    }

    else {

    echo '<pre>';

    echo print_r($this->aAnimData, true);

    echo print_r($this->aSoundData, true);

    echo '</pre>';

    }

    // Close the file, its no longer needed. =(

    fclose($this->act);

    }

    • Upvote 1
  10. Oups the latest thing I wrote was wrong:

    				fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

    extract( unpack("lnumExtraInfo", fread($this->act,0x04) ) );

    $extraInfos = array();

    for ( $i=0; $i<$numExtraInfo; ++$i ) {

    $extraInfos[$i] = unpack( "x2/VExtXOffsetX/VExtXOffsetY/x2", fread($this->act,10) );

    }

    Now working.
  11. Did you even read what I wrote ? :(

    fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want

    $extrainfo = unpack('lbool', fread($this->act, 0x04));// Extras infos (if you need)

    fseek($this->act, 0x10 * $extrainfo['bool'], SEEK_CUR);

    If you want to store extrainfo, you have to do this:
    fseek( $this->act, 4, SEEK_CUR ); // sound id, store it in a variable if you want
    
    extract( unpack("lnumExtraInfo", fread($this->fp,0x04) ) );
    $extraInfos = array();
    for ( $i=0; $i<$numExtraInfo; ++$i ) {
        $extraInfos[$i] = unpack( "x4/VExtXOffsetX/VExtXOffsetY/x4", fread($this->act,16) );
    }
    Edit: Why the bbcode is buggy ? :(
×
×
  • Create New...