Thread: [Matrix] Rewritten packet handlers

Page 1 of 2 12 LastLast
Results 1 to 10 of 11
  1. #1 [Matrix] Rewritten packet handlers 
    Registered Member

    Join Date
    Nov 2013
    Posts
    175
    Thanks given
    106
    Thanks received
    1,045
    Rep Power
    131
    Alright, so this is the release of a request presented to me by Rookie. Credits to him for making me aware of how the packets are presently handled in Matrix, and also motivating me to get off my ass and actually do something. After I found out that all the packets were handled with if-statements, I couldn't help but feel sick. So, I rewrote them as per Rookie's request.

    You'll need some things to get you started. You'll need to create a packets package. This will contain two interfaces. One for handling the game logic packets, the other for just the world packets, and also the "Packet Manager", which we will use to assign the opcodes their appropriate handlers. Please excuse the packet sizes, writing this now I just realised that I never got around to making that more concise. Oh well. This is just so that everything is more clean and organised.

    Code:
    package org.murlock.net.packets;
    
    import org.murlock.game.player.LogicPacket;
    import org.murlock.game.player.Player;
    import org.murlock.io.InputStream;
    
    /**
     * @author Belzebu <http://www.rune-server.ee/members/Belzebu>
     */
    public interface LogicPacketHandler {
    
    	/**
    	 * Abstract void that will be implemented into
    	 * our logic packet handlers
    	 * 
    	 * @param packet
    	 * 		The packet to handle
    	 * 
    	 * @param player
    	 * 		The player sending the packet
    	 * 
    	 * @param stream
    	 * 		The input stream of the player
    	 */
    	public void handleLogic(LogicPacket packet, Player player, InputStream stream);
    	
    }
    Code:
    package org.murlock.net.packets;
    
    import org.murlock.game.player.Player;
    import org.murlock.io.InputStream;
    
    /**
     * @author Belzebu <http://www.rune-server.ee/members/Belzebu>
     */
    public interface PacketHandler {
    
    	/**
    	 * Abstract void that will be implemented into
    	 * our logic packet handlers
    	 * 
    	 * @param packetId
    	 * 		The id of the packet to handle
    	 * 
    	 * @param player
    	 * 		The player sending the packet
    	 * 
    	 * @param stream
    	 * 		The input stream of the player
    	 */
    	public void handle(int packetId, Player player, InputStream stream, int length);
    }
    Code:
    package org.murlock.net.packets;
    
    import org.murlock.game.player.LogicPacket;
    import org.murlock.game.player.Player;
    import org.murlock.io.InputStream;
    import org.murlock.net.packets.impl.*;
    import org.murlock.net.packets.impl.chat.*;
    import org.murlock.net.packets.impl.clan.*;
    import org.murlock.net.packets.impl.dialogue.*;
    import org.murlock.net.packets.impl.fc.*;
    import org.murlock.net.packets.impl.friends.*;
    import org.murlock.net.packets.impl.logic.*;
    import org.murlock.net.packets.impl.misc.*;
    import org.murlock.net.packets.impl.region.DoneLoadingRegionPacketHandler;
    
    /**
     * @author Belzebu <http://www.rune-server.ee/members/Belzebu>
     */
    public class PacketManager {
    
    	/**
    	 * A byte array containing the sizes
    	 * of the different packet types
    	 */
    	public static final byte[] PACKET_SIZES = new byte[256];
    
    	/**
    	 * The array of logic packet handlers
    	 */
    	private static final LogicPacketHandler[] logicHandler = new LogicPacketHandler[256];
    
    	/**
    	 * The array of packet handlers
    	 */
    	private static final PacketHandler[] handler = new PacketHandler[256];
    
    	/**
    	 * The constructor for the packet manager,
    	 * which also sets the packet sizes
    	 */
    	public PacketManager() {
    		for (int id = 0; id < 256; id++)
    			PACKET_SIZES[id] = -4;
    		PACKET_SIZES[64] = 8;
    		PACKET_SIZES[18] = 8;
    		PACKET_SIZES[25] = 8;
    		PACKET_SIZES[41] = -1;
    		PACKET_SIZES[14] = 3;
    		PACKET_SIZES[46] = 3;
    		PACKET_SIZES[87] = 6;
    		PACKET_SIZES[47] = 9;
    		PACKET_SIZES[57] = 3;
    		PACKET_SIZES[67] = 3;
    		PACKET_SIZES[91] = 8;
    		PACKET_SIZES[24] = 7;
    		PACKET_SIZES[73] = 16;
    		PACKET_SIZES[40] = 11;
    		PACKET_SIZES[36] = -1;
    		PACKET_SIZES[74] = -1;
    		PACKET_SIZES[31] = 3;
    		PACKET_SIZES[54] = 6;
    		PACKET_SIZES[12] = -1;
    		PACKET_SIZES[23] = 1;
    		PACKET_SIZES[9] = 3;
    		PACKET_SIZES[17] = -1;
    		PACKET_SIZES[44] = -1;
    		PACKET_SIZES[88] = -1;
    		PACKET_SIZES[42] = 17;
    		PACKET_SIZES[49] = 3;
    		PACKET_SIZES[21] = 15;
    		PACKET_SIZES[59] = -1;
    		PACKET_SIZES[37] = -1;
    		PACKET_SIZES[6] = 8;
    		PACKET_SIZES[55] = 7;
    		PACKET_SIZES[69] = 9;
    		PACKET_SIZES[26] = 16;
    		PACKET_SIZES[39] = 12;
    		PACKET_SIZES[71] = 4;
    		PACKET_SIZES[22] = 2;
    		PACKET_SIZES[32] = -1;
    		PACKET_SIZES[79] = -1;
    		PACKET_SIZES[89] = 4;
    		PACKET_SIZES[90] = -1;
    		PACKET_SIZES[15] = 4;
    		PACKET_SIZES[72] = -2;
    		PACKET_SIZES[20] = 8;
    		PACKET_SIZES[92] = 3;
    		PACKET_SIZES[82] = 3;
    		PACKET_SIZES[28] = 3;
    		PACKET_SIZES[81] = 8;
    		PACKET_SIZES[7] = -1;
    		PACKET_SIZES[4] = 8;
    		PACKET_SIZES[60] = -1;
    		PACKET_SIZES[13] = 2;
    		PACKET_SIZES[52] = 8;
    		PACKET_SIZES[65] = 11;
    		PACKET_SIZES[85] = 2;
    		PACKET_SIZES[86] = 7;
    		PACKET_SIZES[78] = -1;
    		PACKET_SIZES[83] = -1;
    		PACKET_SIZES[27] = 7;
    		PACKET_SIZES[2] = 9;
    		PACKET_SIZES[93] = 1;
    		PACKET_SIZES[70] = -1;
    		PACKET_SIZES[1] = -1;
    		PACKET_SIZES[8] = -1;
    		PACKET_SIZES[11] = 9;
    		PACKET_SIZES[0] = 9;
    		PACKET_SIZES[51] = -1;
    		PACKET_SIZES[5] = 4;
    		PACKET_SIZES[45] = 7;
    		PACKET_SIZES[75] = 4;
    		PACKET_SIZES[53] = 3;
    		PACKET_SIZES[33] = 0;
    		PACKET_SIZES[50] = 3;
    		PACKET_SIZES[76] = 9;
    		PACKET_SIZES[80] = -1;
    		PACKET_SIZES[77] = 3;
    		PACKET_SIZES[68] = -1;
    		PACKET_SIZES[43] = 3;
    		PACKET_SIZES[30] = -1;
    		PACKET_SIZES[19] = 3;
    		PACKET_SIZES[16] = 0;
    		PACKET_SIZES[34] = 4;
    		PACKET_SIZES[48] = 0;
    		PACKET_SIZES[56] = 0;
    		PACKET_SIZES[58] = 2;
    		PACKET_SIZES[10] = 8;
    		PACKET_SIZES[35] = 7;
    		PACKET_SIZES[84] = 6;
    		PACKET_SIZES[66] = 3;
    		PACKET_SIZES[61] = 8;
    		PACKET_SIZES[29] = -1;
    		PACKET_SIZES[62] = 3;
    		PACKET_SIZES[3] = 4;
    		PACKET_SIZES[63] = 4;
    		PACKET_SIZES[73] = 16;
    		PACKET_SIZES[38] = -1;
    	}
    
    	/**
    	 * Default method for handling logic packets
    	 * 
    	 * @param packet
    	 * 		The packet to handle
    	 * 
    	 * @param player
    	 * 		The player sending the packet
    	 * 
    	 * @param stream
    	 * 		The input stream of the player
    	 */
    	public static void handleLogic(LogicPacket packet, Player player, InputStream stream) {
    		if (logicHandler[packet.getId()] != null) {
    			logicHandler[packet.getId()].handleLogic(packet, player, stream);
    			return;
    		}
    		logicHandler[0].handleLogic(packet, player, stream);
    	}
    
    	/**
    	 * Default method for handling packets
    	 * 
    	 * @param packetId
    	 * 		The id of the packet
    	 * 
    	 * @param player
    	 * 		The player sending the packet
    	 * 
    	 * @param stream
    	 * 		The input stream of the player
    	 */
    	public static void handle(int packetId, Player player, InputStream stream, int length) {
    		if (handler[packetId] != null) {
    			handler[packetId].handle(packetId, player, stream, length);
    			return;
    		}
    		handler[0].handle(packetId, player, stream, length);
    	}
    
    	/**
    	 * A static constructor, which will assign
    	 * the packet id their appropriate handler
    	 */
    	static {
    		/* Default */
    		logicHandler[0] = new DefaultLogicPacketHandler();
    		handler[0] = new DefaultPacketHandler();
    
    		/* Miscelleneous */
    		handler[5] = new MoveCameraPacketHandler();
    		handler[75] = new InOutScreenPacketHandler();
    		handler[87] = new ScreenPacketHandler();
    		handler[84] = new ClickPacketHandler();
    		handler[4] = new SwitchDetailPacketHandler();
    		handler[22] = new SkillcapeColourIdPacketHandler();
    		handler[16] = new PingPacketHandler();
    		handler[29] = new MoveMousePacketHandler();
    		handler[68] = new KeyTypedPacketHandler();
    		handler[15] = new ReceivePacketCountPacketHandler();
    		
    		/* Regions */
    		handler[33] = new DoneLoadingRegionPacketHandler();
    		
    		/* Dialogues */
    		handler[54] = new ContinueDialoguePacketHandler();
    		handler[59] = new EnterStringPacketHandler();
    		handler[3] = new EnterIntegerPacketHandler();
    		
    		/* Chat */
    		handler[30] = new PublicQuickChatPacketHandler();
    		handler[23] = new ChatTypePacketHandler();
    		handler[36] = new ChatPacketHandler();
    		handler[70] = new CommandPacketHandler();
    		
    		/* Action buttons */
    		handler[61] = new ActionButtonPacketHandler(); //Action Button 1
    		handler[64] = new ActionButtonPacketHandler(); //Action Button 2
    		handler[4] = new ActionButtonPacketHandler(); //Action Button 3
    		handler[52] = new ActionButtonPacketHandler(); //Action Button 4
    		handler[81] = new ActionButtonPacketHandler(); //Action Button 5
    		handler[18] = new ActionButtonPacketHandler(); //Action Button 6
    		handler[10] = new ActionButtonPacketHandler(); //Action Button 7
    		handler[25] = new ActionButtonPacketHandler(); //Action Button 8
    		handler[91] = new ActionButtonPacketHandler(); //Action Button 9
    		handler[20] = new ActionButtonPacketHandler(); //Action Button 10
    		
    		/* Friends */
    		handler[51] = new AddFriendPacketHandler();
    		handler[8] = new RemoveFriendPacketHandler();
    		handler[72] = new SendFriendPrivateMessagePacketHandler();
    		handler[79] = new SendFriendQuickChatMessagePacketHandler();
    		
    		/* Friends Chat */
    		handler[1] = new JoinFriendChatPacketHandler();
    		handler[32] = new KickFriendChatPacketHandler();
    		handler[41] = new ChangeFriendChatPacketHandler();
    		
    		/* Clans */
    		handler[7] = new ClanNamePacketHandler();
    		handler[74] = new ClanForumThreadPacket();
    
    		/* Walking packets */
    		logicHandler[12] = new WalkingPacketHandler();
    		logicHandler[83] = new WalkingPacketHandler();
    
    		/* Item packets */
    		handler[73] = new ItemOnItemPacketHandler();
    		handler[27] = new ItemExaminePacketHandler();
    		logicHandler[24] = new TakeItemPacketHandler();
    		
    		/* Player options */
    		logicHandler[14] = new FirstPlayerOptionPacketHandler();
    		logicHandler[53] = new SecondPlayerOptionPacketHandler();
    		handler[77] = new PlayerTradeOptionPacketHandler();
    		handler[46] =  new AcceptTradePacketHandler();
    
    		/* NPC Options */
    		logicHandler[66] = new AttackNpcPacketHandler();
    		logicHandler[9] = new FirstClickNpcPacketHandler();
    		logicHandler[31] = new SecondClickNpcPacketHandler();
    		handler[92] = new NpcExaminePacketHandler();
    		
    		/* Interfaces */
    		logicHandler[40] = new InterfaceOnPlayerPacketHandler();
    		logicHandler[65] = new InterfaceOnNpcPacketHandler();
    		handler[56] = new CloseInterfacePacketHandler();
    		handler[26] = new SwitchInterfaceItemPacketHandler();
    
    		/* Object */
    		logicHandler[11] = new FirstClickObjectPacketHandler();
    		logicHandler[2] = new SecondClickObjectPacketHandler();
    		logicHandler[76] = new ThirdClickObjectPacketHandler();
    		logicHandler[42] = new ItemOnObjectPacketHandler();
    		handler[47] = new ObjectExaminePacketHandler();
    		
    	}
    }
    After that's been added, replace your WorldPacketsDecoder with the following:
    Code:
    package org.murlock.net.decoders;
    
    import java.util.logging.Logger;
    
    import org.murlock.game.player.LogicPacket;
    import org.murlock.game.player.Player;
    import org.murlock.io.InputStream;
    import org.murlock.net.Session;
    import org.murlock.net.packets.PacketManager;
    import org.murlock.utils.Utils;
    
    public final class WorldPacketsDecoder extends Decoder {
    
    	/**
    	 * The logger instance
    	 */
    	private static final Logger logger = Logger.getLogger(WorldPacketsDecoder.class.getName());
    	
    	public final static int ACTION_BUTTON1_PACKET = 61;
    	public final static int ACTION_BUTTON2_PACKET = 64;
    	public final static int ACTION_BUTTON3_PACKET = 4;
    	public final static int ACTION_BUTTON4_PACKET = 52;
    	public final static int ACTION_BUTTON5_PACKET = 81;
    	public final static int ACTION_BUTTON6_PACKET = 18;
    	public final static int ACTION_BUTTON7_PACKET = 10;
    	public final static int ACTION_BUTTON8_PACKET = 25;
    	public final static int ACTION_BUTTON9_PACKET = 91;
    	public final static int ACTION_BUTTON10_PACKET = 20;
    
    	private final static int WALKING_PACKET = 12;
    	private final static int MINI_WALKING_PACKET = 83;
    	public final static int RECEIVE_PACKET_COUNT_PACKET = 15;
    
    	private final static int OBJECT_CLICK1_PACKET = 11;
    	private final static int OBJECT_CLICK2_PACKET = 2;
    	private final static int OBJECT_CLICK3_PACKET = 76;
    	private final static int NPC_CLICK1_PACKET = 9;
    	private final static int NPC_CLICK2_PACKET = 31;
    	private final static int ATTACK_NPC = 66;
    	private final static int PLAYER_OPTION_1_PACKET = 14;
    	private final static int PLAYER_OPTION_2_PACKET = 53;
    	private final static int ITEM_TAKE_PACKET = 24;
    	private final static int SWITCH_INTERFACE_ITEM_PACKET = 26;
    	private final static int INTERFACE_ON_PLAYER = 40;
    	private final static int INTERFACE_ON_NPC = 65;
    	private final static int ITEM_ON_OBJECT_PACKET = 42;
    
    	private Player player;
    
    	public WorldPacketsDecoder(Session session, Player player) {
    		super(session);
    		this.player = player;
    	}
    
    	@Override
    	public void decode(InputStream stream) {
    		while (stream.getRemaining() > 0 && session.getChannel().isConnected()
    				&& !player.hasFinished()) {
    			int packetId = stream.readUnsignedByte();
    			if (packetId >= PacketManager.PACKET_SIZES.length) {
    				System.out.println("PacketId " + packetId
    						+ " has fake packet id.");
    				break;
    			}
    			int length = PacketManager.PACKET_SIZES[packetId];
    			if (length == -1)
    				length = stream.readUnsignedByte();
    			else if (length == -2)
    				length = stream.readUnsignedShort();
    			else if (length == -3)
    				length = stream.readInt();
    			else if (length == -4) {
    				length = stream.getRemaining();
    				System.out.println("Invalid size for PacketId " + packetId
    						+ ". Size guessed to be " + length);
    			}
    			if (length > stream.getRemaining()) {
    				length = stream.getRemaining();
    				System.out.println("PacketId " + packetId
    						+ " has fake size. - expected size " + length);
    				break;
    			}
    
    			int startOffset = stream.getOffset();
    			processPackets(packetId, stream, length);
    			stream.setOffset(startOffset + length);
    		}
    	}
    
    
    	public void processPackets(final int packetId, InputStream stream,
    			int length) {
    
    		player.setPacketsDecoderPing(Utils.currentTimeMillis());
    		if (packetId == WALKING_PACKET
    				|| packetId == MINI_WALKING_PACKET
    				|| packetId == ITEM_TAKE_PACKET
    				|| packetId == PLAYER_OPTION_2_PACKET
    				|| packetId == PLAYER_OPTION_1_PACKET || packetId == ATTACK_NPC
    				|| packetId == INTERFACE_ON_PLAYER
    				|| packetId == INTERFACE_ON_NPC
    				|| packetId == NPC_CLICK1_PACKET
    				|| packetId == NPC_CLICK2_PACKET
    				|| packetId == OBJECT_CLICK1_PACKET
    				|| packetId == SWITCH_INTERFACE_ITEM_PACKET
    				|| packetId == OBJECT_CLICK2_PACKET
    				|| packetId == OBJECT_CLICK3_PACKET
    				|| packetId == ITEM_ON_OBJECT_PACKET)
    			player.addLogicPacketToQueue(new LogicPacket(packetId, length,
    					stream));
    		else {
    			PacketManager.handle(packetId, player, stream, length);
    			logger.info("Unhandled packet " + packetId + ", expected size: "
    					+ length + ", actual size: " + PacketManager.PACKET_SIZES[packetId]);
    		}
    	}
    
    	public Player getPlayer() {
    		return player;
    	}
    
    }
    In your player class, you'll need to replace the processLogicPackets method (as the previous way is now redundant):
    Code:
    public void processLogicPackets() {
    		LogicPacket packet;
    		while ((packet = logicPackets.poll()) != null) {
    			InputStream stream = new InputStream(packet.getData());
    			PacketManager.handleLogic(packet, this, stream);
    		}
    	}
    Also, add the following while you're at it:
    Code:
    /**
    	 * The player's chat type
    	 */
    	private int chatType = 0;
    	
    	/**
    	 * Sets the chat type of the player
    	 * 
    	 * @param chatType
    	 * 		The chat type
    	 */
    	public void setChatType(int chatType) {
    		this.chatType = chatType;
    	}
    
    	/**
    	 * Gets the chat type of the player
    	 * 
    	 * @return
    	 * 		The player's chat type
    	 */
    	public int getChatType() {
    		return chatType;
    	}
    Due to the sheer amount of packets, obviously I can't post all the code here. Instead, I've archived them for you. You can download them here: [Only registered and activated users can see links. ]

    This was written for the Feather 667 release, so I don't know if you'll have to change anything to match your server. Obviously the packaging. Also, you'll need to instantiate the PacketManager class. You could, for example, just add the following to your launcher:
    Code:
    private static PacketManager packetManager = new PacketManager();
    If I've forgotten anything (which is quite possible), just let me know
    Reply With Quote  
     

  2. #2  
    Registered Member
    shed's Avatar
    Join Date
    Dec 2010
    Posts
    1,837
    Thanks given
    504
    Thanks received
    575
    Rep Power
    5000
    nice contribution
    "We don't submit to terror. We make the terror." - #FU2016
    Reply With Quote  
     

  3. #3  
    Donator

    Join Date
    Mar 2013
    Posts
    1,777
    Thanks given
    324
    Thanks received
    386
    Discord
    View profile
    Rep Power
    318
    Thanks I guess...
    Reply With Quote  
     

  4. #4  
    0x2P | ~0x2P
    Google rapes bing's Avatar
    Join Date
    May 2013
    Posts
    598
    Thanks given
    120
    Thanks received
    62
    Rep Power
    0
    A lot of code organization is needed and tiny mistakes but it isn't a big issue. Good Job overall
    Reply With Quote  
     

  5. #5  
    Registered Member
    maffia-rpg's Avatar
    Join Date
    Jul 2011
    Posts
    2,776
    Thanks given
    587
    Thanks received
    759
    Rep Power
    120
    Your naming is wrong. The interface you call 'LogicPacketHandler' is just a 'LogicPacket'.
    The class you call 'LogicPacketManager' should be the 'LogicPacketHandler'.

    I wouldn't call it a 'LogicPacket' in the first place.
    Quote Originally Posted by Nando View Post
    why would I care about trying to get you to care about me homosexual?
    back to coding shit revisions
    1080% lost.
    Reply With Quote  
     

  6. #6  
    0x2P | ~0x2P
    Google rapes bing's Avatar
    Join Date
    May 2013
    Posts
    598
    Thanks given
    120
    Thanks received
    62
    Rep Power
    0
    Quote Originally Posted by maffia-rpg View Post
    Your naming is wrong. The interface you call 'LogicPacketHandler' is just a 'LogicPacket'.
    The class you call 'LogicPacketManager' should be the 'LogicPacketHandler'.

    I wouldn't call it a 'LogicPacket' in the first place.
    He wanted to name it that so he did other people can change it, not that big of a deal but thanks I guess for pointing it out.
    Reply With Quote  
     

  7. #7  
    The internet's reject
    Andy's Avatar
    Join Date
    Jun 2007
    Age
    31
    Posts
    4,376
    Thanks given
    148
    Thanks received
    1,522
    Rep Power
    3133
    Quote Originally Posted by maffia-rpg View Post
    Your naming is wrong. The interface you call 'LogicPacketHandler' is just a 'LogicPacket'.
    The class you call 'LogicPacketManager' should be the 'LogicPacketHandler'.

    I wouldn't call it a 'LogicPacket' in the first place.
    Naming is left to the namer.
    What am I watching now?

    Reply With Quote  
     

  8. Thankful users:


  9. #8  
    Super Donator


    Join Date
    Mar 2009
    Age
    25
    Posts
    1,393
    Thanks given
    316
    Thanks received
    408
    Rep Power
    608
    wtf is the difference between a logicpacket and a normal packet ffs, they should all be processed @ 600ms intervals
    Reply With Quote  
     

  10. Thankful user:


  11. #9  
    Super Donator


    Join Date
    Mar 2009
    Age
    25
    Posts
    1,393
    Thanks given
    316
    Thanks received
    408
    Rep Power
    608
    Quote Originally Posted by Belzebu View Post
    There is no difference, it's just the naming that dkk used to differentiate between the two types. Yes, they are all processed at the same cycle time.
    then you should of removed that shit and written a system that uses one class as apposed to two
    Reply With Quote  
     

  12. #10  
    Super Donator


    Join Date
    Mar 2009
    Age
    25
    Posts
    1,393
    Thanks given
    316
    Thanks received
    408
    Rep Power
    608
    Quote Originally Posted by Belzebu View Post
    opposed*

    I don't see the need to. As we've already established, they are both processed the same, and I had wanted to keep the naming conventions similar to that as was already implemented. No need for arguing on this thread, if you don't like it, don't use it.
    wasnt arguing and there is no need to correct me on my spelling i dont give 2 shits.

    was giving you a tip saying there is no point having 2 classes that do exactly the same thing, talk about bloated code lmao
    Reply With Quote  
     

Page 1 of 2 12 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Packet handler/Java error? HELP!
    By Samair1 in forum Help
    Replies: 8
    Last Post: 12-28-2010, 11:43 AM
  2. 562 packet handler
    By zshorty123 in forum Help
    Replies: 0
    Last Post: 12-13-2010, 01:33 AM
  3. Packet Handler?
    By kalo93 in forum Help
    Replies: 4
    Last Post: 08-30-2010, 10:51 PM
  4. Replies: 2
    Last Post: 05-13-2010, 12:34 PM
  5. Basic packet handler
    By Link in forum Tutorials
    Replies: 6
    Last Post: 11-29-2009, 08:06 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •