Jump to content
Sign in to follow this  
Ben

World selection skipping & improving login authentication

Recommended Posts

Hello everyone,

Many Club Penguin Private Servers make use of only one world server, this is generally a good thing as it keeps all of your players together and helps encourage social interaction between players in the game. Club Penguin has always given the players the option to choose which world server they want to join, but if there is only one world server, what's the point in providing users the option?

I wrote some code which will give you the ability to remove the world selection screen from the game entirely, this not only makes login faster thanks to not having to ask the user for input, but also because we no longer need a login server at all, we just authenticate with the world server and call it a day.

Here is the actionscript code for the dependency shockwave flash file:

import com.clubpenguin.util.*;
import com.clubpenguin.crypto.*;
 
var SHELL = _global.getCurrentShell();
var AIRTOWER = _global.getCurrentAirtower();
 
AIRTOWER.connectToLogin = function(in_username, in_pass, login_response) {
    if (!AIRTOWER.is_logged_in) {
        AIRTOWER.on_login_response = login_response;
        AIRTOWER.username = in_username;
        AIRTOWER.password = in_pass;
        AIRTOWER.server.onConnection = Delegate.create(AIRTOWER, AIRTOWER.handleLoginConnection);
        AIRTOWER.server.onExtensionResponse = Delegate.create(AIRTOWER, AIRTOWER.onAirtowerResponse);
        AIRTOWER.server.debug = true;
        AIRTOWER.addListener(AIRTOWER.HANDLE_LOGIN, AIRTOWER.handleOnLogin);
        var _local1 = SHELL.getWorldCrumbs();
        var _local2;
        var _local3;
        for (_local2 in _local1) {
           _local3 = _local1[_local2];
           break;
        }
        SHELL.world_id_holder = _local3.id;
        AIRTOWER.server.connect(_local3.ip, _local3.port);
    } else {
        AIRTOWER.shell.$e("connectToLogin() -> Your already logged in! Cant login again");
    }
}
 
AIRTOWER.handleLoginConnection = function(success) {
    if(success) {
        AIRTOWER.login();
    } else {
        AIRTOWER.on_login_response(false);
    }
}
 
AIRTOWER.login = function() {
    AIRTOWER.server.login("w1", AIRTOWER.username, AIRTOWER.getLoginHash());
}
 
AIRTOWER.handleOnLogin = function(obj) {
    AIRTOWER.removeListener(AIRTOWER.HANDLE_LOGIN, AIRTOWER.handleOnLogin);
    AIRTOWER.shell.setMyPlayerId(obj[1]);
    AIRTOWER.playerId = obj[1];
    AIRTOWER.login_key = obj[2];
    AIRTOWER.on_login_response(true);
    AIRTOWER.is_logged_in = true;
    SHELL.gotoState(SHELL.PLAY_STATE);
}
 
SHELL.connectToWorld = function() {
    var _local1 = SHELL.getWorldById(SHELL.world_id_holder);
    SHELL.showLoading((SHELL.getLocalizedString("Joining") + " ") + _local1.name);
    AIRTOWER.joinWorld();
}
 
AIRTOWER.handleJoinWorld = function(obj) {
    AIRTOWER.removeListener(AIRTOWER.JOIN_WORLD, AIRTOWER.handleJoinWorld);
    var _local6 = Boolean(Number(obj[1]));
    var _local3 = Boolean(Number(obj[2]));
    var _local5 = Boolean(Number(obj[3]));
    var _local4 = Boolean(Number(obj[4]));
    AIRTOWER.on_world_response(true, _local6, _local3, _local5, _local4);
    AIRTOWER.on_world_response = undefined;
}
 
AIRTOWER.joinWorld = function() {
    AIRTOWER.server.onConnectionLost = Delegate.create(AIRTOWER, AIRTOWER.handleLostConnection);
    var _local2 = new Array();
    _local2.push(AIRTOWER.playerId);
    _local2.push(AIRTOWER.login_key);
    _local2.push(AIRTOWER.shell.getLanguageAbbriviation());
    if (AIRTOWER.isRedemption) {
        AIRTOWER.addListener(AIRTOWER.REDEMPTION_JOIN_WORLD, handleJoinRedemption, AIRTOWER);
        AIRTOWER.send(AIRTOWER.REDEMPTION, AIRTOWER.REDEMPTION_JOIN_WORLD, _local2, "str", -1);
        return(undefined);
    }
    AIRTOWER.on_world_response = SHELL.connectToWorldResponse;
    AIRTOWER.addListener(AIRTOWER.JOIN_WORLD, AIRTOWER.handleJoinWorld);
    AIRTOWER.send(AIRTOWER.PLAY_EXT, (AIRTOWER.NAVIGATION + "#") + AIRTOWER.JOIN_WORLD, _local2, "str", -1);
}
 
AIRTOWER.getLoginHash = function() {
    return SHA256.hash(AIRTOWER.password);
}

Now, since the way that the client authenticates is slightly different, your server will need to be modified to support it. In this case, I actually made mine use SHA256 hashing (thanks @Kevin) instead of MD5. There is also now no real need for a random key. Note you do not need to use SHA256 with this, it was just what I decided I wanted to use, if you want to go back to MD5, or use another authentication method entirely, you can do so by overriding 'AIRTOWER.getLoginHash'.

With this code the client connects directly to the first localized world server in the world crumbs, performs the handshake, hashes the password and authenticates, then proceeds to get the players information.

This is how the world server auth works:

recv: <msg t='sys'><body action='verChk' r='0'><ver v='153' /></body></msg>
send:  <msg t='sys'><body action='apiOK' r='0'></body></msg>
recv: <msg t='sys'><body action='login' r='0'><login z='w1'><nick><![CDATA[Ben_]]></nick><pword><![CDATA[SHA256]]></pword></login></body></msg>

A couple of actionscript classes are required for this code to export, everything is zipped below, you'll also find a pre-exported version in there if you are happy to use mine.

The SWF file goes in /play/v2/client/ of your media server, please add it to dependencies.json too:

{
	
	boot: [
		{
			id: 'airtower',
			title: 'Communication'
		},
		{
			id: 'sentry',
			title: 'Communication'
		}
	],
	
	login: [
		{
			id: 'cookies',
			title: 'Login Screen'
		},
		{
			id: 'login',
			title: 'Login Screen'
		}
	],
	
	join: [
		{
			id: 'engine',
			title: 'Engine'
		},
		{
			id: 'interface',
			title: 'Interface'
		},
		{
			id: 'gridview',
			title: 'Gridview'
		},
		{
			id: 'mail',
			title: 'Mail'
		},
		{
			id: 'book',
			title: 'Mail'
		},
		{
			id: 'stampbook',
			title: 'StampBook'
		}
	],
	
	create: [
		{
			id: 'create',
			title: 'Create Penguin'
		}
	],
	
	merch: [
		{
			id: 'app',
			folder: 'merch/',
			title: 'Communication'
		}
	]
	
}

End result:

DXm.gif

  • Please suggest improvements, specifically ways to improve the security, I'm sure this is pretty half-assed.
  • Please ask if you're having troubles getting the server sided stuff to work.
  • Works on only legacy private servers, not the new client, but could be easily ported. I'll do it if I ever find the time.

 

cookies.zip

  • Like 7

Share this post


Link to post
Share on other sites
Sign in to follow this  

×