A user login system for UDK

This is the placeholder tutorial for all of you who are patiently awaiting the Kiosk final touches, I have had to rework a large portion of the kiosks in light of some issues with forwarding input around, but hopefully this evening ill have v2 working and ill be able to get back to work on the tutorial.

In the down time though, I have found a number of people are asking about user login systems and how to pull them off. The truth is, its not that difficult.People are just very good at being afraid to do things they have not already done. For anyone who has written a web app you likely already took many of the steps, but here we go with a very simple one.

The concepts in this tutorial can easily be ported around to more involved things like stats tracking, achievements as well as providing a bit of persistence across servers if you are so inclined.

As a final word of caution, it is important that you take this tutorial further than the words I share. It is intermediate, meaning you will be putting together some advanced UnrealScript elements, such as delegates, exec functions and TCPLink, as well as using PHP on your web server to do all of the dirty work. This is not an optimal setup, however.

  1. If you are doing a networked game you will likely want to make this server side, and let UDK handle the networking to any important actors.
  2. All validation and commenting will be omitted for brevity

Down to business

We have to start out with the TCPLink class, because the work we do here will absolutely make or break our system. This class is going to encapsulate all of the complexities of HTTP Requests, and do some preliminary response cleanup for us to lighten the load when we need to actually work with it.

/**
 * @author      Bob Chatman bob@gneu.org
 * @version     2011-08-10
 * @since       July 2011 Release
 */

class HTTPLink extends TCPLink
    Config(BetterTech);

var config string Host;
var config string Page;
var config int HostPort;

var bool RequestInProgress;
var bool ShouldPOST;

var string Action;
var array Data;

var string LastResponse;

Here is the header for our class. We define a few interesting elements:

  1. RequestInProgress (RIP) is a boolean we test to see if there is a request in progress, since it doesn’t really make sense to modify a request when its being processed.
  2. ShouldPost is a flag to toggle between GET and POST requests. I tend to keep non trivial submissions as POST because I am a zealot for taking steps to protect websites when possible. This is an example of “security through obscurity,” which is not security at all, but it will help you cut down on submissions, as well as a couple other things I will show you later.
  3. Action is the name of the Action to post. I moved it to a variable primarily in earlier tests to ensure it is set.
  4. The Data array is used to consolidate the Key=Value pairs that you will find in HTTPRequests, but I will go into this more in just a few minutes.

Note the three config variables. This is done to allow us to move the values of these variables into the config files. You will likely have a few of these classes hiding around and you can stick entries as the following into your config file:

[BetterTech.HTTPLink]
Host=bettertech.gneu.org
HostPort=80
Page=superTest.php

If you would like more information on config variables please follow these links:

UnrealScript Language Reference | Configuration Files

Request Execution, Response Delegation

Next up is a delegate, which we are defining now to provide a route for the successful request to be processed by any function in any class. Another method could be to do something similar to how exceptions are written in Java – new class for anything different and pass a class reference for success/fail which will encapsulate the functionality and build a response tree, but I find that if I can consolidate them it makes modifying the code a bit easier. And truthfully, most people don’t even know that delegates exist, so enjoy your function pointers =)

delegate OnResponse( string act, array res );

This is actually pretty important. act is the Action that was previously set, allowing you to test for it if you choose, and res is an array of the response values. One thing that is important to define early on is a set of standards for your Requests and Responses. For the purposes of this tutorial Requests will follow HTTP standards, because it is easy for PHP to work with them, and HTTP Responses will be comma separated tokens provided in an expected order. You will quickly see the benefit to following these standards as we progress.

TCPLink begins with a call to Resolve, but, we are trying to wrap up a couple other bits of functionality at the same time. Instead of expecting the user to have to set the RIP flag and delegate reference up we are going to create a new function:

function bool Execute(delegate ResponseDelegate)
{
    `Log("Attempting to Resolve " $ Host);

    if (RequestInProgress || Action == "")
        return false;

    super.Resolve(Host);

    OnResponse = ResponseDelegate;
    RequestInProgress = true;
    return true;
}

Although it has been simplified a bit, note the tests. If there is a request in progress or we have no action, we fail out, and all of our assignments are done after that. This is a primary task, and it helps to avoid modification mid request. Once the Resolve function has been triggered all hell breaks loose and the class is on cruise control… Resolved or ResolveFailed gets called, neither of which are really very important for this but ill include them for completeness:

event Resolved( IpAddr Addr )
{
    `Log("Resolved to " $ IpAddrToString(Addr));
    Addr.Port = HostPort;

    if (!Open(Addr))
        `Log("Open failed");
}

event ResolveFailed()
{
    Cleanup(false);

    `Log("Unable to resolve " $ Host);
}

Their names speak for themselves – Resolved occurs when the location is resolved and ResolveFailed is called when its not. I have included a couple log messages for your debugging use. Cleanup is actually a function i use to clear out the relevant variables and flags to allow for a new query to be made:

function Cleanup(bool clearData)
{
    if (clearData)
    {
        Data.length = 0;
        Action = default.Action;
    }

    ShouldPOST = default.ShouldPOST;
    RequestInProgress = default.RequestInProgress;
}

We swap in the default values for the elements and clear out our data array. You will note that when resolving fails I don’t clear out the data, this is to allow for resubmission if needed.

Sending the request

We have already probably done more than most people are comfortable doing, but things are just getting fun. Our next function is Opened which is responsible for making successive calls to SendText to send the related message across the wire to your webserver. In order for this function to be called all of the above must have already been done, and now things get a little hairy.

HTTPRequests have a very specific format, and deviation from it is actually a bit difficult to debug – all you get is a failed response. I have tested the following out at length and it has worked for anything I have thrown at it.

event Opened()
{
    local string text;

    if (ShouldPOST)
        SendText("POST /" $ Page $ " HTTP/1.0");
    else
        SendText("GET /" $ Page $ " HTTP/1.0");

    SendText("Host: " $ Host);
    SendText("User-Agent: BetterTechClient/1.0");

    Data.AddItem("Action=" $ Action);

    if (Data.length > 0)
    {
        JoinArray(Data, text, "&", true);

        `Log(text);

        SendText(
            "Content-Type: application" $
            "/x-www-form-urlencoded");
        SendText(
            "Content-Length: " $ len(text) $
            chr(13) $ chr(10));
        SendText(text);
    }

    SendText("Connection: Close" $ chr(13) $ chr(10));

    `Log("Query Sent.");
}

As you can see this is a bit more involved, but our work above is already helping loads. We will add a couple convenience functions in a moment to improve developer usability.

I wont really go into how things are setup here, or why, but you are free to look up the HTTP request headers and formatting in and read about them if you choose. The two things I will note for you is my use of the User-Agent header, which is another security through obscurity layer that we will utilize later, and JoinArray which converts our data into a string, glued together with & characters.

Handling the response

The class is nearly finished now, all we need to do is put that delegate to use, clean up and a couple convenience functions.

If you compile the code as is, your requests will fail, outright. This is primarily because each of those SendText calls is incomplete. Each time that SendText is called above you are actually supposed to append \r\n to the end of it. Fortunately enough for you, we have function overloading and can abstract that away.

function int SendText(string s)
{
    return super.SendText(s $ chr(13) $ chr(10));
}

Adding data to the array is should also be a simple process, as should setting the action:

function bool appendData(string s)
{
    if (RequestInProgress)
        return false;

    Data.AddItem(s);

    return true;
}

function bool setAction(string s)
{
    if (RequestInProgress)
        return false;

    Action = s;

    return true;
}

Do note the pattern here, these functions will fail if a request is in progress.

We are likely to want to wipe all of our request data once we close our connection. I have added a log message as well.

event Closed()
{
    Cleanup(true);

    `Log("Closing Link.");
}

Our final function is ReceivedText, which is triggered when we get a response from the server. It comes back in the form of an HTTP Response, which is similar in format to our HTTP Request mentioned earlier.

event ReceivedText( string Text )
{
    LastResponse = Text;

    // `log(Text);

    Text = Split(Text,
        chr(13) $ chr(10) $ chr(13) $ chr(10),
        true);

    if (OnResponse != none)
        OnResponse(Action, SplitString(Text, ",", true));
}

There is some not so complicated string parsing elements in here but please feel free to read up on them if you are not already in line. Because of the complexity of this class I have taken the liberty of attaching it at the very end.

Triggering the request

So, we have the framework in place, there are now only a couple of steps between us and being able to log in. Because the scaleform end of things is quite insignificant I am going to move to a PlayerController Exec function, and the paired delegate so i can explain them at the same time:

exec function Authenticate(string user, string password)
{
    if (MyLink == None)
        MyLink = Spawn(class'HTTPLink');

    MyLink.setAction("Authenticate");

    MyLink.appendData("User=" $ user);
    MyLink.appendData("Password=" $ password);

    MyLink.Execute(onAuthenticate);
}

function onAuthenticate(string act, array res)
{
    if (res[0] == "Success")
    {
        `Log("[REQUEST] Authentication has succeeded");

        if (PlayerReplicationInfo != none)
        {
            BTPRI(PlayerReplicationInfo).SetPlayerName(res[1]);
            SetTimer(AutoUpdatePRIInterval, true, 'UpdatePRI');
        }
        // else
        // DisplayNotice("Player has logged in successfully!");

        UserToken = res[2];
        UserName = res[1];

        SaveConfig();
    }
    else
        `LogMessage("Authentication has failed");
}

All n all, this is pretty straight forward. You will see above that the Authenticate function takes two strings, passes them in to the link and executes it. Our abstraction earlier really simplifies our interactions. Just for illustration purposes, ill show you a second request execution:

exec function SavePRI()
{
    if (MyLink == None)
        MyLink = Spawn(class'HTTPLink');

    if (!BTPRI(PlayerReplicationInfo).shouldSave)
        return;

    MyLink.setAction("SavePRI");

    MyLink.appendData("User=" $ UserName);
    MyLink.appendData("Token=" $ UserToken);
    MyLink.appendData("Fields[]=cash|" $
        BTPRI(PlayerReplicationInfo).cash);
    MyLink.appendData("Fields[]=savings|" $
        BTPRI(PlayerReplicationInfo).savings);

    MyLink.Execute(onSavePRI);
}

exec function onSavePRI(string act, array res)
{
    `LogMessage(act);
    `LogMessage(res[0]);

    if (res[0] == "Success")
        `Log("[REQUEST] SavePRI has succeeded");
    else
        `LogMessage("SavePRI request has failed");
}

Both of these requests illustrate the flexibility of the link class, as well as the benefit of using delegates in this situation. We also gain some flexibility by making these exec functions, because all that you need to do is run a console command and you can do that from practically anywhere – hence why dealing with Scaleform is not important.

You can see here that I am storing my data in the PRI, but you can do anything you want with it.

This is really the end of the UnrealScript. Now we can move over to the PHP end of things, where things get easier.

On the Web Server

We are nearly done, thankfully. It may seem like a lot of work, but its really just some abstraction to clean up the interaction and a couple of function calls. On the web server side we can break things into a few aspects

  1. Switch based on the action
  2. Sanitize and validate your input
  3. Do what you need to do with it
  4. Respond

You may find them similar to steps you had seen in web development in the past, and for those of you who picked up on that, congratulations. All of your PHP development is only as difficult as anything going on in web development. You can find tutorials on how to connect to Databases, file IO and so forth with a little creative googling.

<?php 

switch($_POST['Action'])
{
case 'Authenticate':
// ... Sanitize and validate
if ($_POST['User'] == 'Bob_Gneu' &&
    sha1($_POST['Password']) == '...')
{
    print "Success,Bob_Gneu,BT_42";
}
else
{
    print "Failure";
}
break;
?>

And with that you should have a working user login system. I recommend you read a couple of my other posts as well, just to make sure you are up to speed on web development expectations.

If you are a copy and paste developer I hope you get ill, but I’ve attached the full HTTPLink example file. If you have questions or comments you know where they go =D

Download

Enjoy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值