Announcement

Collapse
No announcement yet.

[VIDEO][CODE] An Easy, Free, Online Game Hosting System Without Steam

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    [VIDEO][CODE] An Easy, Free, Online Game Hosting System Without Steam

    Scope of This Tutorial: How to Enable Players to Host / Join Games for your Project, for Free

    When you want to offer an online gaming service to your players, you need a way to tell them what dedicated and listen servers are available at any time, and users need to be able to retrieve this list and join games of their own choosing, or host their own game and have other players see it.

    There are many online gaming services that require having your project be “approved” (steam greenlight for example) and/or come with a lot of code to integrate and a need to interact extensively with people who know a lot about the specific online platform you are wanting to integrate.

    Wouldn't it be nice to have an online game hosting service where you didnt need to have your UDK project presented to the world and approved before you could start having players host and join games at will?

    ~~~

    Gemini, Free Online Game Hosting Service

    William Gaul has created and is offering to the world a free way to have players host and join games that use your UDK game/project! This free hosting service is called Gemini

    I am indeed talking about a free system to automate the process of players being able to list games they are hosting and have other players join those games for your UDK project


    * Please Note * if you want to use William's Gemini Service for a commercial game which can expect to have a higher load on his system, there is a Commercial Game Gemini Plan

    Gemini is free for non-commercial developers however, so you can try it out any time, long before your UDK project goes commercial!

    ~~~

    Code Offered To You In this Tutorial:

    1. All the code you need to get a free Sever Listing Service working with your game, called Gemini, authored and run by William Gaul.

    2. Several functions I have written to work with Gemini to handle hosting and joining Gemini Games through repeated calls to Gemini to retrieve or post data. Helps when internet connection is inconsisent to simplify what the user has to do to ensure a connection to Gemini via your Game UI.

    ~~~

    Video Footage Demonstrating Use of Free Online Server Hosting Service

    Here are two videos of actual multiplayer footage using my game and William's Gemini Service.

    William is the other player

    This footage is demonstrating that you do not need any steam integration to actually host and join multiplayer games via an online server listing, and you can do this for free!


    Actual Multiplayer Footage Using Gemini Only as Server Listing System



    Second Video is First Response Below

    ~~~

    Video Frame Rates

    Neither video is a reflection of lag/fps for the actual gameplay, both videos were recorded and processed at a lower framerate than I was experiencing in-game.

    The actual game play was very fluid and fun!

    ~~~

    Code for You For Running Gemini In Your Game

    Here's the unmodified original code download from William's site:

    William's Gemini Starter Kit


    ~~~

    Modified Gemini Code

    I am providing the modified version of his code that I used in this video, and included William's code so that this tutorial is complete code-wise unto itself. But all credit for these 2 classes below, and authorship, belongs to William Gaul.

    I modified William's starter kit code for this class to work with playercontroller class instead of gameinfo, since I wanted to be able to see the Gemini listing while already hosting a listen server

    I only modified two words in GeminiOnlineService.uc, the entire rest of this code and all credit for this code goes to William.


    Code:
    class GeminiOnlineService extends Actor;
    
    var GeminiLinkClient ConnClient;
    var string AccountCode;
    var string AccountPass;
    
    /* Queue of POST/GET to handle multiple requests and prevent them from overriding
     * each other. They are in FIFO order, so PostQueue[0] is always executed next.
     */
    var array<string> PostQueue;
    
    /* Reference to your game class. Replace this with the actual name of your game
     * class. This completes the three-way handshake between player, the link client,
     * and the online service.
     */
    var YOURCONTROLLERCLASS playerPC;
    
    /* Allows this class to call functions in another class when connection is done.
     * The function bound to this delegate must have no parameters or return types.
     * If parameters or return types are needed, they must be wrapped in the function
     * that is called.
     */
    delegate NotifyDelegate();
    
    function RegisterNotifyDelegate(delegate<NotifyDelegate> MyNotifyDelegate)
    {
    	NotifyDelegate = MyNotifyDelegate;
    }
    
    function Initialize(YOURCONTROLLERCLASS MG)
    {
    	playerPC = MG;
    	ConnClient = Spawn(class'GeminiLinkClient');
    	ConnClient.Initialize(self);
    }
    
    
    ///THE ACTUAL POST AND GET FUNCTIONS///
    function GeminiGet(string webpage, string message, bool bNotify)
    {
    	if(ConnClient.bIsBusy)
    	{
    		`log("[GeminiOnlineService] Client is busy, queueing GET request");
    		PostQueue.AddItem("get~" $ webpage $ "~" $ message $ "~" $ string(bNotify));
    	}
    	else
    	{
    		`log("[GeminiOnlineService] Getting Data");
    		ConnClient.TransmitData(false, bNotify, webpage, message);
    	}
    }
    
    function GeminiPost(string webpage, string message, bool bNotify)
    {
    	if(ConnClient.bIsBusy)
    	{
    		`log("[GeminiOnlineService] Client is busy, queueing POST request");
    		PostQueue.AddItem("post~" $ webpage $ "~" $ message $ "~" $ string(bNotify));
    	}
    	else
    	{
    		`log("[GeminiOnlineService] Posting Data: " $ message);
    		ConnClient.TransmitData(true, bNotify, webpage, message);
    	}
    }
    ///END THE ACTUAL POST AND GET FUNCTIONS///
    
    
    function string FormatPost(string Input)
    {
    	return "&content=" $ Repl(Input, " ", "+");
    }
    
    
    ///POST AND GET FOR SERVICE///
    function GetFromService(optional bool bNotify)
    {
    	GeminiGet("/service.jsp?", "pass="$AccountPass$"&code="$AccountCode, bNotify);
    }
    
    function PostToService(string Message, optional bool bNotify)
    {
    	GeminiPost("/sign", "checker="$AccountPass$"&code="$AccountCode$FormatPost(Message), bNotify);
    }
    ///END POST AND GET FOR SERVICE///
    
    
    ///POST AND GET FOR SERVER///
    /* Gets a list of all servers currently on Gemini. They will be added to ListServers in VictoryBallGameInfo.uc
     * You should use ListServers to display a list of all servers in a matchmaking screen. When the
     * player picks a server, grab its IP by doing VictoryBallGameInfo.ListServers[ROW].ServerIP. Then you can do
     * ConsoleCommand("open "$IP).
     */
    function GetFromServer()
    {
    	GeminiGet("/browser.jsp?", "pass="$AccountPass$"&code="$AccountCode$"&view=false", true);
    }
    
    /* Adds a server to the server list. You do not need to specify the IP; this will be
     * automatically calculated for you when the server is added. This can be called by
     * calling VictoryBallGameInfo.ServiceBrowser.PostToServer() using a reference to VictoryBallGameInfo when the
     * host player decides to host a match.
     */
    function PostToServer( 
    	optional string ServerName = "VictoryGame",
    	optional int ServerPort = 7778,
    	optional string ServerPass = "null", 
    	optional int GametypeIndex = 1,
    	optional string MapList = "VictoryBallMultTest2", 
    	optional int CurPlayers = 0, 
    	optional int MaxPlayers = 32, 
    	optional int Ranked = 0, 
    	optional bool bInGame = true)
    {
    	GeminiPost("/register", "password="$AccountPass$"&code="$AccountCode$
    			"&port="$ string(ServerPort)$
    			"&name="$ ServerName$
    			"&pass="$ ServerPass$
    			"&type="$ string(GametypeIndex)$
    			"&list="$ MapList$
    			"&curr="$ string(CurPlayers)$
    			"&maxp="$ string(MaxPlayers)$
    			"&rank="$ string(Ranked)$
    			"&bing="$ string(bInGame), true);
    }
    
    /* Removes a server from the browser. This assumes that you cannot have more than one
     * server running at the same IP.
     */
    function RemoveServer()
    {
    	GeminiPost("/register", "password="$AccountPass$"&code="$AccountCode$"&pass=delete", false);
    }
    ///END POST AND GET FOR SERVER///
    
    
    function RetryPost()
    {
    	local string NextPost;
    	local array<string> Parts;
    	if(PostQueue.Length != 0) {
    		if(PostQueue[0] != "")
    		{
    			NextPost = PostQueue[0];
    			PostQueue.Remove(0,1);
    			ParseStringIntoArray(NextPost, Parts, "~", true);
    			if(Parts[0] == "get")
    			{
    				GeminiGet(Parts[1], Parts[2], bool(Parts[3]));
    			}
    			else
    			{
    				GeminiPost(Parts[1], Parts[2], bool(Parts[3]));
    			}
    		}
    	}
    }
    
    
    ///GEMINI SPECIAL FUNCTIONS///
    function FlushMessages(int Index, optional bool bNotify)
    {
    	PostToService("flush" $ Index, bNotify);
    }
    
    function ReplaceMessage(int Index, string NewMsg, optional bool bNotify)
    {
    	PostToService("replace" $ Index $ "," $ NewMsg, bNotify);
    }
    
    function DeleteMessage(int Index, optional bool bNotify)
    {
    	PostToService("delete" $ Index, bNotify);
    }
    
    function SetFieldSize(int Size, optional bool bNotify)
    {
    	PostToService("setfieldsize" $ Size, bNotify);
    }
    
    function PerformOp(int Index, int Oper, int Value, optional bool bNotify)
    {
    	PostToService("perform" $ Index $ ",op" $ Oper $ ",num" $ Value, bNotify);
    }
    
    function AppendToMessage(int Index, string ToAppend, optional bool bNotify)
    {
    	PostToService("append" $ Index $ "," $ ToAppend, bNotify);
    }
    ///END GEMINI SPECIAL FUNCTIONS///
    
    
    /* Called when the ConnClient has received a response from Gemini.
     * At the moment, responses are limited to GET requests only.
     */
    function HandleGetResponse(string webpage, string Text)
    {
    	if(webpage == "/service.jsp?")
    	{
    		playerPC.HandleServiceData(Text);
    	}
    	else if(webpage == "/browser.jsp?")
    	{
    		playerPC.HandleServerData(Text);
    	}
    }
    
    /* Called when the ConnClient has completed a transmission, but only if
     * bNotify was true. This should be used for things like reducing the player
     * count before exiting the game: since the operation MUST be completed before
     * a quit is called, you can put ConsoleCommand("quit"); in here and know
     * that your game will safely exit.
     */
    function OnConnectionCompleted(string message)
    {
    	// For now just call the delegate.
    	NotifyDelegate();
    }
    
    defaultproperties
    {
    	AccountCode = "1234567"; // First 7 characters of your code
    	AccountPass = "pass"; // Your pass
    }
    ~~~

    TcpLink Client Class

    //this code is straight from William's starter kit
    Code:
    class GeminiLinkClient extends TcpLink;
    
    var string TargetHost;
    var int TargetPort;
    
    /* A reference to the online service, which is the only class this class actually
     * communicates with. In this way GeminiOnlineService serves as a middleman...the
     * only way for data to get from Game <--> Gemini is through it.
     */
    var GeminiOnlineService ServiceBrowser;
    
    // The page on the TargetHost site to resolve to, and the data to send.
    var string webpage, message;
    
    // True if a POST, false if a GET.
    var bool bPost;
    
    // Whether to notify ServiceBrowser upon transmission completion.
    var bool bNotifyOnComplete;
    
    // To avoid multiple TCP connections from overriding each other.
    var bool bIsBusy;
    
    function Initialize(GeminiOnlineService GOS, optional string THost = "geminionlinegs.appspot.com", optional int TPort = 80)
    {
    	TargetHost = THost;
    	TargetPort = TPort;
    	ServiceBrowser = GOS;
    }
    
    /* Begin the transmission process. You must specify whether this is a POST, whether to
     * notify upon transmission end, what webpage to send the data, and what data to send.
     * If no data is specified, the null string "" will be sent.
     */
    function TransmitData(bool bPostData, bool bNotify, string page, optional string data)
    {
    	bIsBusy = true;
    	webpage = page;
    	message = data;
    	bNotifyOnComplete = bNotify;
    	bPost = bPostData;
    	`log("[GeminiLinkClient] Resolving: " $ TargetHost);
    	Resolve(TargetHost);
    }
    
    event Resolved(IpAddr Addr)
    {
    	`log("[GeminiLinkClient] " $ TargetHost $ " resolved to " $ IpAddrToString(Addr));
    	Addr.Port = TargetPort;
    	`log("[GeminiLinkClient] Bound to port: " $ BindPort());
    	if(!Open(Addr))
    		`log("[GeminiLinkClient] Open failed");
    }
    
    event ResolveFailed()
    {
    	`log("[GeminiLinkClient] Unable to resolve " $ TargetHost);
    	// You could retry resolving here if you have an alternative remote host.
    }
    
    event Opened()
    {
    	`log("[GeminiLinkClient] TCP connection opened");
    	if(bPost)
    	{
    		SendText("POST " $ webpage $ " HTTP/1.0" $ chr(13)$chr(10));
    		SendText("Host: " $ TargetHost $ chr(13)$chr(10));
    		SendText("User-Agent: GeminiLinkClient/1.0" $ chr(13)$chr(10));
    		SendText("Content-Type: application/x-www-form-urlencoded" $ chr(13)$chr(10));
    		SendText("Content-Length: " $ Len(message) $ chr(13)$chr(10));
    		SendText(chr(13)$chr(10));
    		SendText(message $ chr(13)$chr(10));
    	}
    	else
    	{
    		SendText("GET " $ webpage $ message $ " HTTP/1.0" $ chr(13)$chr(10));
    		SendText("Host: " $ TargetHost $ chr(13)$chr(10));
    		SendText(chr(13)$chr(10));
    	}
    	SendText(chr(13)$chr(10));
    	SendText(chr(13)$chr(10));
    	`log("[GeminiLinkClient] End TCP connection");
    }
    
    event Closed()
    {
    	`log("[GeminiLinkClient] TCP connection closed");
    	// Connection closed, so we are not busy any more.
    	bIsBusy = false;
    	if(bNotifyOnComplete)
    		ServiceBrowser.OnConnectionCompleted(message);
    	ServiceBrowser.RetryPost();
    }
    
    event ReceivedText(string Text)
    {
    	// If this is a get request.
    	if(!bPost)
    	{
    		ServiceBrowser.HandleGetResponse(webpage, Text);
    	}
    }
    
    defaultproperties
    {
    	bIsBusy = false;
    }
    ~~~

    Player Controller Class Functions Provided by William

    These functions, which go in your controller class, are also entirely from William's starter kit:

    Code:
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Gemini
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    var GeminiOnlineService GeminiService;
    
    struct Server
    {
    	var string ServerName;
    	var string ServerIP;
    	var string ServerPass;
    	var int Gametype;
    	var int NumPlayers;
    	var int MaxPlayers;
    	var int Ranked;
    	var bool bInGame;
    };
    
    var array<Server> ListServers;
    
    //~~~~~~~~~~~~~~~
    // Rama Gemini Vars
    //~~~~~~~~~~~~~~~
    var int GeminiRetryAttempts;
    var int GeminiHOSTRetryAttempts;
    var string GeminiGameName;
    
    //~~~~~~~~~~~~~~
    // Gemini Functions
    //~~~~~~~~~~~~~~
    final function TriggerRemoteKismetEvent(name EventName)
    {
    	local array<SequenceObject> AllSeqEvents;
    	local Sequence GameSeq;
    	local int i;
    	GameSeq = WorldInfo.GetGameSequence();
    	if(GameSeq != none)
    	{
    		GameSeq.FindSeqObjectsByClass(class'SeqEvent_RemoteEvent', true, AllSeqEvents);
    		for(i = 0; i < AllSeqEvents.Length; i++)
    		{
    			if(SeqEvent_RemoteEvent(AllSeqEvents[i]).EventName == EventName)
    				SeqEvent_RemoteEvent(AllSeqEvents[i]).CheckActivate(WorldInfo, None);
    		}
    	}
    }
    
    final function HandleServiceData(string Text)
    {
    	local int i,len;
    	local array<string> Messages;
    
    	ParseStringIntoArray(Text, Messages, "<@>", true);
    	len = Messages.length;
    	Messages[len - 1] = Left(Messages[len - 1],InStr(Messages[len - 1],"<form"));
    	for(i = 1; i < len; i++)
    		`Log("[GeminiLinkClient] Message " $ i $ ": " $ Messages[i]);
    }
    
    final function HandleServerData(string Text)
    {
    	local int i,len;
    	local array<string> Messages;
    	local array<string> ServerParts;
    
    	ListServers.length = 0;
    	ParseStringIntoArray(Text, Messages, "<@>", true);
    	len = Messages.length;
    	Messages[len - 1] = Left(Messages[len - 1],InStr(Messages[len - 1],"<form"));
    	for(i = 1; i < (len-1); i++)
    	{
    		`Log("[GeminiLinkClient] Server " $ i $ ": " $ Messages[i]);
    		ParseStringIntoArray(Messages[i], ServerParts, "~", true);
    		ListServers.Add(1);
    		ListServers[i-1].ServerName=ServerParts[0];
    		ListServers[i-1].ServerIP=ServerParts[1];
    		ListServers[i-1].ServerPass=ServerParts[3];
    		ListServers[i-1].Gametype=int(ServerParts[4]);
    		ListServers[i-1].NumPlayers=int(ServerParts[6]);
    		ListServers[i-1].MaxPlayers=int(ServerParts[7]);
    		ListServers[i-1].Ranked=int(ServerParts[8]);
    		ListServers[i-1].bInGame=bool(Left(ServerParts[9], 4));
    	}
    }
    
    //~~~~~~~~~ END GEMINI
    My Gemini Functions for Player Controller Class

    Below I am providing you with my own Gemini Functions for

    Hosting:
    1. starting a listen server for current map, after attempting several times to start a new server listing with Gemini and ensure that it gets listed before fully launching the new game.

    Joining:
    2. Retrieving a server listing from Gemini, trying for about 15 seconds before determining whether any servers are currently being listed / connection to Gemini could be successfully established. useful for inconsistent internet connections especially.

    Cleanup
    3. Clearing any Gemini Listing for your computer's IP when a single player game is launched, reduces resources used by your Gemini account helping Will to offer his awesome free service more easily to more people.

    Code:
    //Cleanup
    Simulated Event PostBeginPlay() {
    	//very important line
    	super.postbeginplay();
    	
    	//pre-begin play does not work properly on client machines for this code
    	//so I moved this to post-begin play
    	
    	//Gemini Initializing
    	GeminiService = Spawn(class'GeminiOnlineService');
    	GeminiService.Initialize(self);
    	
    	//Loading in Single Player? 
    	//	Remove Gemini Listing
    	if (worldinfo.netmode == NM_Standalone) {
    		GeminiService.RemoveServer();
    	}
    	
    	//when game is loaded as a listen server netmode is not standalone
    	//so the new server listing is not removed
    	
    }
    	
    	
    	
    //==============================
    // Rama Gemini Hosting Functions
    //==============================
    
    //hosting game part 3, load multiplayer map as listen server
    final function startHostingGame() {
    		
    	//assumes already in the level want to host for, use getPackageName
    	consolecommand("open"@String(WorldInfo.GetPackageName())$"?Listen");
    }
    
    //hosting game part 2, wait to confirm game was created
    //recursive function
    final function waitForGeminiToRegisterGame() {
    	local server s;
    	
    	//end case
    	if (GeminiHOSTRetryAttempts > 30) {
    		
    		//restart counter
    		GeminiHOSTRetryAttempts = 0;
    		
    		//end timer cycle
    		ClearTimer('waitForGeminiToRegisterGame');
    		
    		clientmessage("Your Game Could Not Be Registered With Gemini Service");
    		
    		return;
    	}
    	//===================
    	
    	//check if our game name got registered
    	
    	//refresh listing
    	GeminiService.GetFromServer();
    	
    	//search thru all games
    	foreach ListServers(s) {
    		
    		//when it shows, then actually launch listen server game
    		if (s.ServerName == GeminiGameName) {
    			
    			//clear timer
    			ClearTimer('waitForGeminiToRegisterGame');
    			
    			//display msg
    			clientmessage("Hosting New Game...");
    			
    			//prepare to change level
    			SetTimer(0.1, false, 'startHostingGame');
    			return;
    		}
    	}
    	
    	//no games found?
    	if(GeminiHOSTRetryAttempts <= 30){
    		clientmessage("Registering Your Game With Gemini Service..."@GeminiHOSTRetryAttempts$"/"$"30");
    		
    		//recursive call
    		SetTimer(0.5, false, 'waitForGeminiToRegisterGame');
    		
    		GeminiHOSTRetryAttempts++;
    	}
    }
    
    //hosting game part 1, name entered
    final function hostGeminiGame(string gameName) {
    	
            //store global ref to current game name
    	GeminiGameName = gameName; 
    	
    	//post to server with system date as well
    	GeminiService.PostToServer(GeminiGameName);
    	
    	//wait to see if game gets registered
    	waitForGeminiToRegisterGame();
    }
    
    //~~~~~~~~~~~~~~~~~~~ END OF RAMA GEMINI HOSTING FUNCTIONS ~~~~~~~~~~~~~~~~~~
    
    //==============================
    // Rama Gemini Joining Functions
    //==============================
    
    final function joinLANGame() {
    	
    	clientmessage("joining"@ "127.0.0.1");
    	
    	consolecommand("open"@ "127.0.0.1");
    }
    
    final function joinGame(string theIP) {
    	
    	//"open ip" is how you join an existing game
    	//just need to make sure port forwarding is working
    	/*
    	"forward ports 7777 and 7778 on your router (google port forwarding).
    	"If you cant get a listing, forward 6500, 7787, 13000, 27900."
    	*/
    	
    	clientmessage("joining"@theIP);
    	
    	consolecommand("open"@theIP);
    }
    
    //search for games
    
    //repeatedly fills the list servers array
    //over about 15 seconds, stops once Gemini Service responds
    //and fills the ListServer array
    
    //call listGeminiServers() on a repeating timer to perpetually refresh
    final function searchForHostedGames() {
    	
    	//end case
    	if (GeminiRetryAttempts > 30) {
    		
    		//restart counter
    		GeminiRetryAttempts = 0;
    		
    		//end timer cycle
    		ClearTimer('searchForHostedGames');
    		
          clientmessage("No Games Found At Present, Consider Hosting Your Own! :)");
    		
    		SetTimer(2,true,'listGeminiServers');
    		
    		return;
    	}
    	//===================
    	
    	//refresh
    	listGeminiServers();
    	
    	//no games found yet? keep looking
    	if(ListServers.length < 1 && GeminiRetryAttempts <= 30){
    		clientmessage("Searching for Active Games..."@GeminiRetryAttempts$"/"$"30");
    		SetTimer(0.5, false, 'searchForHostedGames');
    		GeminiRetryAttempts++;
    	}
    	
    	//perpetual relisting
    	else {
    		
    		//found games, now keep refreshing list
    		//SetTimer(3,true,'listGeminiServers');
    		return;
    	}
    	/*
    	clientMessage(	"Click on the Game name you wish to join!\n"$
    		"If you have trouble entering the game, make sure to forward\n"$
                    "ports TCP & UDP 7777 and 7778 on your router (google port forwarding).\n"$
                    "If you cant get a listing, forward TCP_UDP 6500, 7787, 27015 (steam sockets"
    	);
    	*/
    }
    
    //list games
    
    //fills the ListServers array
    final function listGeminiServers() {
    	
    	//Debugging via Internet Browser
    	//see active games online via
    //http://www.geminionlinegs.appspot.com/browser.jsp?pass=YOURPASS&code=YOURGEMINI_ID_CODE_FIRST_7_CHARS&view=true
    	
    	//fills the ListServers array to update server list
    	//must always call each time to refresh list
    	GeminiService.GetFromServer();
    	
    }
    
    //~~~~~~~~~~ End Rama Gemini Joining Functions ~~~~~~~~~~~~~~~~
    ~~~

    Joining A Game From Gemini List

    Once you iterate over ListServers array and display them using your chosen method, you need to use the following code to join a specific game:

    //in the controller class, using the joinGame function from above code:
    Code:
    joinGame(ListServers[chosenGameIndex].ServerIP);
    ~~~

    Summary of How This All Works

    Basically to join a game you need to know the IP address of the person who is hosting the listen server game, or the dedicated server's IP.

    Then you can use the UDK console command “open IPaddress“ to join that game.

    Gemini enables you to list the IP addresses of people using your game to host listen servers / dedicated servers, and thus you can internally use the console command “open IP” when players click on one of the games you retrieve from the Gemini Game Services

    ~~~

    Port Forwarding

    To be a listen server or dedicated server host you need to have certain ports forwarded on your router, if you are using one, or a modem that is router + modem in one.

    Check out http://portforward.com/ for more info

    Make sure that you forward TCP and UDP for 7777 and 7778, and 27015 if using Steam sockets.

    Usually this amounts to typing 192.168.1.1 or 192.168.0.1 into your web browser and entering your username and password for your router. Then you can access port forwarding optiosn.

    Here's William's Gemini page on Port Forwarding

    ~~~

    FireWalls

    For your OS firewall settings you'll see that most UDK installs are forwarding TCP and UDP for all ports by program executable.

    So you will want to create new rules for your own game exectuable, make sure to pick whether you are using win32 or win64 udk.exe, and then make similar rules to what you see the default UDK install uses.

    ~~~

    Connection Issues

    Here's a helpful thread on using the console command Open IP, started by the Networking Expert Solid Snake

    Using Open IP with UDK

    ~~~

    Summary

    Using William Gaul's awesome free Gemini service you can setup an easy and free server listing to enable players to host and join games for your UDK game project!



    Rama

    #2
    Dear Community,

    Here's the second video

    First-ever Actual Multiplayer Footage, also Using Gemini


    Rama

    Comment


      #3
      You guys are both so awesome for making and sharing this (Tutorial, and service) <3

      Now I totally have to learn replication and make my game replicate in the future D:

      Comment


        #4
        Awesome! I've had his Gemini Kit laying around for a while just haven't taken the time to get into it. I'm glad you did, thanks for sharing!

        Comment


          #5
          Hee Hee!

          Glad you two enjoyed the tutorial!



          Rama

          Comment


            #6
            just awsome ! can't wait to figure out what's that gemini thing xD

            Comment


              #7
              Originally posted by bwhit View Post
              Awesome! I've had his Gemini Kit laying around for a while just haven't taken the time to get into it. I'm glad you did, thanks for sharing!
              same here
              thanks Rama

              Comment


                #8
                Great to hear from you Tegleg!

                Hope it helps!

                I will be doing more research on this whole topic and will keep you updated on my findings

                Rama

                Comment


                  #9
                  Originally posted by evernewjoy View Post
                  Great to hear from you Tegleg!

                  Hope it helps!

                  I will be doing more research on this whole topic and will keep you updated on my findings

                  Rama
                  Do you use RootMotion in your game? After seeing this tutorial I spent the whole day trying to make my current game replicate, and right now it "does", but the rootmotion is replicating terribly cluky (When the guest is near/in-view of the host) and not at all (when the guest is outside the field of view of the host). For replicating the animations I'm using Chokster's tutorial method.
                  Apparently right now the Client is running his simulation of the animation, which is completely ignored if he's not in the field of view of host and his position just resets ignoring the rootmotion, and the host also runs a simulation of his own rootmotion on the client's which updates very laggy. I'll record a video better explaining the problem and get some snippets of my code and open a topic with some questions regarding that tomorrow, just posting here wondering if you've used replication+rootmotion and ran into a similar problem/knows of a solution D:

                  Comment


                    #10
                    I dont use rootmotion no,

                    but are you might wanna try

                    Code:
                    defaultproperties
                    {
                      bAlwaysRelevant	 = true
                      bOnlyDirtyReplication = false
                    }

                    for the actors using root motion to see if that causes any change in behavior


                    I cant help much with root motion sorry but feel free to ask me other replication-related questions!



                    Rama

                    PS: if you get good results try putting only dirty rep back on as it is very expensive to set it to false

                    Comment


                      #11
                      great work, cant wait to learn it.

                      Comment


                        #12
                        I can only any signs of gemini when I play in editor, anything else and nothing shows up in the logs about gemini

                        [0035.97] ScriptLog: [GeminiOnlineService] Posting Data: password=xxxx&code=44c700c&pass=delete
                        [0035.97] ScriptLog: [GeminiLinkClient] Resolving: geminionlinegs.appspot.com
                        [0036.01] ScriptLog: [GeminiLinkClient] geminionlinegs.appspot.com resolved to 74.125.132.141:0
                        [0036.01] ScriptLog: [GeminiLinkClient] Bound to port: 58778
                        [0036.09] ScriptLog: [GeminiLinkClient] TCP connection opened
                        [0036.09] ScriptLog: [GeminiLinkClient] End TCP connection


                        I tried using a different client to connect using open IP and got this in my log

                        [0037.36] NetComeGo: Close TcpNetDriver_0 TcpipConnection_0 74.125.132.141:7777 04/25/14 15:02:33
                        [0037.36] Exit: TcpNetDriver_0 shut down

                        but couldnt connect

                        Comment

                        Working...
                        X