Простейший сервер на ASP.NET (C#) для Flash-игр и их связка

Привет всем мастерам отечественного игростроя и не только.

В этой небольшой статье я хочу рассказать о том, как я делал, делаю и возможно буду делать серверы для своих игр в связке Flash AS3 + Windows Server + ASP.NET (C#) + MSSQL Server. Ничего нового или уникального в этой статье нет. Это один из десятков вариантов как быстро сделать серверную часть и связку с ней для клиента. Применяю описанный метод в многопользовательских социальных играх (и приложениях). Но, осмелюсь предположить, что и в однопользовательских играх это вполне применимо, особенно когда речь идет о сохранении профиля на сервере и/или сборе всяческой статистики.

flash + .net

Кому может быть полезна статья?

Честно говоря, совсем не уверен что она полезна в полной мере этого слова, но возможно слегка познавательна в плане “как можно сделать простенький сервер и как он вообще будет держаться в бою”.

Какие преимущества этого подхода по сравнению с другими?

У описанного подхода, основное преимущество – это его адская простота. Он совершенно не требует углубленного знания языка и серверных технологий, но я думаю, что с быдлокодерством он не имеет много общего. Развернуть такой сервер вопрос 10 минут (при наличии готового «шаблона»). А самое главное, на мой взгляд – такой подход не отвлекает от самого вкусного в игрострое, а именно от создания самой игры. Вполне возможно, что кто-то из новичков захочет использовать его в своих проектах.

И так, что нам нужно:

  1. Сервер: любой web сервер на базе Windows с настроенным IIS.
  2. Visual Studio: на ваш выбор, но я делаю примеры в 2010, хотя и четверти ее прелестей не использую.
  3. Среда разработки Flash AS3 приложений: любая ваша любимая.
Для новичков порекомендую небольшую статью по настройке IIS под Windows Server 2009 R2.

Начнем?

Для связи клиента с сервером я выбрал простейший метод HTTP запросов, в ответ на которые сервер генерирует JSON. Почему не XML? А мне он не нравится неудобочитаемостью и громоздкостью, но это вообще не принципиально, важно, что HTTP.

Схема всей связки:
Схема

Примерный сценарий работы клиента:
При запуске игры, клиент должен получить все необходимые сведения о пользователе соц. сети и далее уже запросить данные на основе полученного ID у нашего сервера, для чего клиент запрашивает данные по примерно такой ссылке:
http://{адрес_вашего_сервера}[/{путь_до _приложения}]/{название_обработчика}.aspx[?{какие-то_параметры]}
Сразу оговорюсь, оставлять возможность посылать параметры в GET, вроде как, считается дурным тоном, но для отладки это очень удобно. Поэтому, если будете использовать нечто подобное в релизе, просто добавьте фильтр на сервере на обработку только POST запросов (ниже в описании сервера, я покажу как). A уж на флеше отправить POST запрос тоже самое что и GET, отличие только в этом:
myURLRequest.method = URLRequestMethod.POST;
myURLRequest.method = URLRequestMethod.GET;

Причем в реальной игре помимо данных о пользователе (его профиль) перед всеми прочими запросами к нашему серверу нужно получить данные о самой игре – ее общие настройки, не касающиеся пользователя, всевозможные массивы с данными об имеющихся в магазине товарах, услугах, акциях и т.п.

Хм, надо бы оживить примером:
31.186.99.154/oligarh/gp.aspx?action=getFullUserProfile&nid=1&uid=12787999

Что мы тут видим кроме адреса и названия .aspx страницы для обработки запросов? Правильно, параметры:
action=getFullUserProfile – в этом параметре указано какую ф-ию сервера хотим вызвать и собственно какие данные получить.
nid=1 – в этом указываем в какой социалке запущено приложение.
uid=12787999 – а тут ID этого пользователя данные которого запрашиваем.

А что же мы получаем в ответ? А вы вставьте вышеуказанный урл в адресную строку браузера, нажмите enter и увидите нечто похожее на это:

{
    "error":0,
    "st":"28.03.2012 15:51:09",
    "userInfo":{
        "id":"12787999",
        "level":5,
        "experience":303,
        "bluff":15,
        "money":"-20",
        "vouchers":3,
        "lastVisit":"09.03.2012 10:46:11",
        "booster":{
            "type":1,
            "npd":"",
            "time":0
        }
    },
    "userBusiness":[
        {
            "id":712488,
            "businessID":2,
            "level":1,
            "state":2,
            "npd":"23.01.2012 10:58:51",
            "time":-5633528,
            "eProfits":1,
            "vouchers":0
        }
    ],
    "userBluffs":[
        1
    ],
}

Теперь клиенту осталось лишь применить эти данные и все готово.
Ну да, конечно надо все это обработать как-то … а как?
Слегка подумав, я остановился на 2-х маленьких классах: DataAccessor и DataProvider.
Полагаю, DataAccessor стоит разобрать по полочкам, а в DataProvider-е просто посмотрим логику.

DataAccessor
Это тот самый класс где мы прописываем ф-ии для доступа к конкретным данным из нашей игры, т.е. это некий API враппер. В нем есть служебная часть, которая не меняется от игры к игре и есть остальная часть которая специфичная для каждой игры.

package api
{
  import api.DataProvider;
  import flash.events.Event;
  import flash.utils.getTimer;
  
  public class DataAccessor extends Object
  {
    //DataAccessor не умеет запрашивать данные напрямую у сервера, и поэтому
    //активно использует возможности DataProvider-а
    private var server: DataProvider;
    
    //Тут храним постоянно урл нашего шлюза
    private var m_serverURL: String;
    
    //Тут храним постоянно id нашей социалки
    private var m_netID: int;
    
    //Тут храним все полученные от нашего сервера данные
    private var m_data: Object;
    
    public function DataAccessor ( serverURL: String, netID: int )
    {
      //При вызове конструктора передаем ему урл нашего сервера и id социалки
      m_serverURL = serverURL;
      m_netID = netID;
      server = new DataProvider ( serverURL );
    }
    
    public function get serverURL (): String
    {
      //Нужно на случай если мы захотим в приложении узнать где наш сервер
      return m_serverURL;
    }
    
    public function get data (): Object
    {
      //Часто используется, при любой попытке прочитать полученные данные из приложения
      return m_data;
    }
    
    private function applyNewData ( dp: DataProvider, onRequestCompleteListener: Function = null ): void
    {
      //В этой ф-ии мы проверяем что нам отдал DataProvider, вызываем ф-ию применения новых данных,
      //и оповещаем всех кто просил об этом.
      //Но для начала проверим чтобы data не были null
      //мне было удобно таким образом проверять получали ли мы хоть раз данные или нет
      if ( m_data == null )
        m_data = new Object ();

      if ( dp.errorText != "" )
      {
        //Если в DataProvider-е была ошибока какая-то (в 99,99% это timeout, поэтому можно тут
        //проверить этот факт и повторить запрос, но мне пока такое не требовалось)
        if ( onRequestCompleteListener )
          //Если при вызове ф-ии DataAccessor-а напередавали callback то вызываем ее и передаем
          //объект с ошибкой -1 ("-N" - внутренние, "N" - серверные, "0" - все нормально)
          onRequestCompleteListener ( { error: -1, errorText: dp.errorText } );
        return;
      }
      if ( dp.resultObject.error != 0 )
      {
        //Если в DataProvider-е не было ошибок
        if ( onRequestCompleteListener )
          //Опять же вызываем callback если он был и передаем ему полученные данные
          onRequestCompleteListener ( dp.resultObject );
        return;
      }
      
      var changeArray: Array = syncNewData ( dp.resultObject );
      
      if ( changeArray.length > 0 )
        //если в данных произошли изменения то мы должны оповестить всех подписчиков
        dispatchEvent ( new SyncEvent ( SyncEvent.SYNC, false, false, changeArray ) );
      
      if ( onRequestCompleteListener )
        //Опять же вызываем callback если он был и передаем ему полученные данные
        onRequestCompleteListener ( dp.resultObject );
    }
    
    private function syncNewData ( newData: Object ): Array
    {
      //В этой ф-ии мы применяем новые данные и генерим массив с путями изменившихся данных
      //Но в текущей версии генерация путей не доработана, и мы априори считаем, что данные
      //в ветке поменялись если таковые вообще пришли
      var changeArray: Array = new Array ();
      for ( var item: String in newData )
      {
        if ( m_data [ item ] != newData [ item ] )
        {
          m_data [ item ] = newData [ item ];
          changeArray.push ( item );
        }
      }
      return changeArray;
    }

    public function getFullUserProfile ( userID: String, fromID, onRequestCompleteListener: Function ): Boolean
    {
      //А это как раз пример API ф-ии для получения данных через наш DataAccessor
      //Таких ф-ий может быть сколько угодно много, например: getSomeUserData, setSomeUserData, checkSomeData...
      //Для того чтобы запросить данные создаем новый DataProvider
      var dp: DataProvider = new DataProvider ( m_serverURL );
      //Далее просим его принять наш запрос и в результате получаем Boolean (принял наш запрос DataProvider или нет)
      return dp.execFunction
      (
        //Имя ф-ии на сервере (параметр action в HTTP запросе)
        "getFullUserProfile",
        //Все остальные параметры необходимые для выполнения ф-ии на сервере
        new Array ( { name: "nid", value: m_netID }, { name: "uid", value: userID } ),
        //В этом месте, наверняка, многие захотят меня пнуть кованым сапогом по какому-нибудь чувствительному месту,
        //но я очень торопился когда это писал и анонимная ф-ия мне довольно хорошо помогла.
        //А что собственно тут делается?
        //А всего навсего передается в DataProvider callback функция (к тому же с параметром Event),
        //которая вызывается при завершении работы провайдера и в свою очередь вызывает applyNewData
        //Этот косяк произошел в следствии переделки DataAccessor-а, а DataProvider остался старый,
        //все как всегда уперлось в отсутствие свободного времени, а потом и желания
        function ( e: Event ) { applyNewData ( e.target as DataProvider, onRequestCompleteListener ); } 
      );
    }
  }
}

Как вы уже догадались специфичная часть представлена одной функцией getFullUserProfile, а все остальное относится к служебной.

DataProvider
В этом классе реализована следующая логика:
  • формирования строки запроса с параметрами (можно и POST и GET)
  • отправка запроса и ожидание ответа с возможностью отсечения по таймауту
  • возврат полученных данных подписчикам (в нашем случае DataAccessor)
Код рассматривать тут не будем, но вы можете посмотреть его в исходниках к статье.
Но, добавлю, что для его работы необходимо подключить JSON библиотеку.

Пора бы нам на сервер заглянуть
Сервер выглядет не сложнее, приведенных выше, двух AS3 классов.
Состоит он в нашем примере из одной aspx страницы gate.aspx и пары классов для обработки данных.
Так как писать полностью рабочий код с БД мне показалось довольно хлопотно для маленькой статьи, я прописал возвращаемый результат прямо в коде.

Рассмотрим каждый файл проекта ближе.

gate.aspx
Это как раз та самая страница aspx, которая принимает запросы от клиента и передает их обработчику нашего API. Смотрим содержимое:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="gate.aspx.cs" Inherits="SimpleGameServer.gate" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%
    try
    {
        //Очищаем полностью формируемый ответ, т.к. в ответе должна быть чистая строка с JSON
        Response.Clear();

        //Тут мы можем отбрить GET запросы
        //if(Context.Request.RequestType != "POST")
            //return;

        //Создаем наш API обработчик
        SimpleGameServer.API api = new SimpleGameServer.API(Context.Request.Params);
        try
        {
            try
            {
                //Пробуем заставить его обработать запрос и вывести ответ в виде JSON
                Response.Write(api.MakeResponse());                
            }
            catch (Exception e)
            {
                //Ошибка при обработке, покажем ее клиенту, пусть репу чешет чего там не так
                Response.Write(api.MakeError(2, e.Message));                
            }                
        }
        finally
        {
            //Все что надо закрываем и уничтожаем 
            api.Dispose();
        }   
    }
    catch (Exception e)
    {
        //Какой-то глобальный трындец, выложил неотлаженный сервер или залилось с ошибками или чего-нибудь еще
        Response.Write(SimpleGameServer.API.MakeGeneralError(1, e.Message));
    }
%>

Похоже что тут все очень просто. Идем дальше.

API.cs
Этот файл содержит класс API, который сразу при его создании выполняет первичную проверку и отбор параметров, запоминает все необходимые данные для дальнейшей обработки и ждет команды чтобы к ней приступить. Как только попросили, сразу вызывает соответствующую цепочку команд и отдает в ответ строку. Смотрим внутренности:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace SimpleGameServer
{
    public class API
    {
        //Тут у нас записано какие параметры мы вообще будем обрабатывать. Это необязательно, но часто полезно. 
        private string[] serveParams = {"action", "uid", "nid", "m", "some1", "some2", "someN"};
        //В этой переменной мы храним текущее действие, которое должны сделать
        private string action;
        //А тут параметры для действия
        private NameValueCollection parameters;

        public API(NameValueCollection rawParams)
        {
            //Сразу же обрабатываем параметры
            InitParameters(rawParams);
        }

        private void InitParameters(NameValueCollection rawParams)
        {
            //Сразу же запоминаем action
            action = rawParams["action"];
            //Заполняем параметры
            parameters = new NameValueCollection();
            foreach (string paramName in serveParams)
            {
                if (rawParams[paramName] != null)
                    parameters.Add(paramName, rawParams[paramName]);
            }
            //Конечно это только демонстрация, и тут можно делать что угодно с параметрами,
            //например, проверить их на попытки SQL-инъекций
        }

        public string MakeResponse()
        {
            //Вызывается напрямую из gate.aspx
            //Проверяем, что нас просят и собственно пробуем сделать
            switch (action)
            {
                case "getGameInfo":
                    return GameRequestProcessor.GetGameInfo(Convert.ToInt32(parameters["nid"]));
                case "getUserInfo":
                    return GameRequestProcessor.GetUserInfo(Convert.ToInt32(parameters["nid"]), Convert.ToInt64(parameters["uid"]));
                case "addUserMoney":
                    return GameRequestProcessor.AddUserMoney(Convert.ToInt32(parameters["nid"]), Convert.ToInt64(parameters["uid"]), Convert.ToInt32(parameters["m"]));
                default:
                    return MakeError(3, "Че за лажа? Забыл какие я запросы могу обрабатывать?");
            }
            //У меня в одной игре тут очень большой switch, да еще и вложенный, чтобы можно
            //было обрабатывать action вида game.getInfo, user.getInfo
            //Заметьте, что данные тут конвертируются в требуемый тип, но это тоже не обязательно,
            //можно все в string оставить, и отложить проверку типов дальнейшим частям сервера
            //все зависит от того что вы захотите получить.
        }

        public static string MakeGeneralError(int code, string message)
        {
            //Тут мы просто показываем JSON с ошибкой
            //Заметьте, что тут используется JSON.GetJSONStringFromDictionary
            //Что совсем не  обязательно, и проще было бы сделать через string.Format
            var result = new Dictionary<string, object> { { "error", code }, { "message", message } };
            return JSON.GetJSONStringFromDictionary(result);
        }

        public string MakeError(int code, string message)
        {
            //Тут мы просто показываем JSON с ошибкой и текущий action
            var result = new Dictionary<string, object> {{"error", code}, {"action", action}, {"message", message}};
            return JSON.GetJSONStringFromDictionary(result);
        }

        public void Dispose()
        {
            //Тут прописываем все что надо завершить как-то по особенному
        }
    }
}

Вроде все по-прежднему легко и понятно.
Коментариев больше чем кода, даже добавить нечего.
Идем дальше.

GameRequestProcessor.cs
А вот это сомое главное в нашей гипотетической игре.
В этом классе содержаться всего лишь примеры для 3-х ф-й, которые должны выполнять манипуляции с данными в БД или ФС или еще где-то, а в конце выдать результат который вы должны получить согласно заложенной вами же логике.
Повторюсь, это просто пример, он даже к БД не обращается, просто показывает статические данные.
Смотрим код:
using System;

namespace SimpleGameServer
{
    public class GameRequestProcessor
    {
        //Тут я добавил константу для ошибки, но обычно их может быть оч много, и лучше их вынести в отдельный класс.
        private static string Error_WrongParams = "Один из указанных параметров выходит за границы допустимых значений. Че куришь? Поделись!!!";

        public static string GetGameInfo(int nid)
        {
            //Судя по названию данная ф-я должна выбрать из БД или ФС информацию об игровых объектах и настройках
            //Заодно тут показано что следует предварительно проверить параметры
            //В реальной игре такой статики конечно не сделать ... уж очень много данных может быть
            if (nid > 0 && nid < 5)
                return "{\"error\":0,\"gameInfo\":{\"goods\":[{\"id\":0,\"name\":\"name1\",\"price\":100},{\"id\":0,\"name\":\"name2\",\"price\":200},{\"id\":2,\"name\":\"name3\",\"price\":300}]}}";
            throw new Exception(Error_WrongParams);
        }

        public static string GetUserInfo(int nid, Int64 uid)
        {
            //Эта ф-я должна выбрать из БД или ФС информацию об игроке по его id
            if (nid > 0 && nid < 5 && uid > 0)
                return string.Format("{{\"error\":0,\"userInfo\":{{\"id\":\"{0}\",\"level\":7,\"exp\":99,\"money\":999}}}}", uid);
            throw new Exception(Error_WrongParams);
        }

        public static string AddUserMoney(int nid, Int64 uid, int money)
        {
            //А эта ф-я просто ради шутки :-), добавляет игроку денег ... всегда хотел что-то подобное в нашей жизни найти ...
            if (nid > 0 && nid < 5 && uid > 0 && money > 0)
                return string.Format("{{\"error\":0,\"userInfo\":{{\"id\":\"{0}\",\"level\":7,\"exp\":99,\"money\":{1}}}}}", uid, 999 + money);
            throw new Exception(Error_WrongParams);
        }
    }
}

В больших проектах одним таким GameRequestProcessor не обойтись, конечно же. Чаще всего присутствует целый набор классов для обработки игровой логики.

Как подключить БД?
В принципе если вы еще никогда не пробовали этого, то могу подсказать, как это сделать и куда рыть дальше.
Т.к. последние 3 года я работаю только с MSSQL да и вообще игры только с ним делал, то и речь пойдет именно о нем.
  • Для начала необходимо создать БД на каком-нибудь сервере, создать пользователя, выдать ему необходимые права на вашу БД.
  • Любым привычным способом для вас залить в БД данные, я пользуюсь SQL Server Managment studio.
  • Далее в файл Web.config вашего проекта необходимо добавить ConnectionString для вашей БД, например такая:
    <connectionStrings>
            <add name="SomeConnectionString" connectionString="Data Source=.\SQLEXPRESS;Persist Security Info=True;User ID=bdusername;Password=userpassword" providerName="System.Data.SqlClient"/>
    

Вот собственно и все, далее где вам необходимо создаете SqlConnection, потом SqlCommand и выполняете ее получая какие-то данные в ответ, переводите их в JSON.
За последним абзацем, конечно много чего скрывается, и не самого простого, например составление запросов, обработка полученных данных, формирование структуры для экспорта в JSON и много чего еще не лежащее на поверхности. Поэтому пока что осмелюсь предложить поизучать поглубже .NET и SQL, а я в свою очередь, если конечно это будет интересно хотябы нескольким тут живущим игроделам, напишу простые примеры как можно легко и быстро научиться общаться с MSSQL.

Исходники

dl.dropbox.com/u/6005934/fgb/sources.rar

PS:

Выражаю свою благодарность игроделу WeslomPo, за приглашение на FGB и помощь в составлении статьи.

UPD1:

(коментировать не получается, помещаю ответ на первый комент тут)
Да, на Java тоже все хотел попробовать, но как-то руки не дошли пока.
Фиксировал максимальные нагрузки порядка 1000 игровых запросов в секунду + запросы картинок, звуков, модулей игры и т.д., причем это явно не никак не напрягало сервер (были и больше нагрузки, но документально не зафиксировали… т.к. сервер спокойно все переваривал). Основная проблема была в качестве канала… когда подключили 100Мбит/сек выделенный, стало проще. Обобщая вышесказанное — IIS очень достойно все это переваривает, и я пока не увидел какого-то непреодолимого предела для нее в рамках игр для социалок.
Более узким местом была, конечно же, БД. Именно она требовала и требует внимания больше всех остальных. Поэтому количество и качество дисков приходится держать на уровне (2-4 на одну популярную игру, разделяя статические данные, и файлы БД по дискам в зависимости от нагрузок). В дополнение скажу, что одна игра, конечно же, сразу работает на 2-4 социалки, поэтому нюансов много по распределению ресурсов, и особенно удобно смотреть чего куда перенести в моменты массированной рекламы.
По реализации логики, возможно отдельной статьей можно рассказать, там тоже много интересного, но опять же, так как я стартовал первую свою игру не зная кто и как это делает, выбрал для себя наиболее знакомый способ — большую часть логики постарался реализовать простейшими однопроходными запросами, и старался хранить их в процедурах и ф-иях (тут мне, конечно, очень не хватало возможностей из Oracle (Возможно как-нибудь применю его, если не перейду на реализацию логики в отдельном вин-сервисе)). Надо сказать, что возможно мой подход устарел. Отчасти, делаю эту статью, я хотел услышать в коментах об альтернативах, чтобы как-то в современную струю влиться.
Т.к. вся логика в SQL, то основной мониторинг сводится к отслеживанию скорости выполнения запросов и их эффективности. Ну и логи IIS помогают распознать ряд неприятностей. Все это делается в пиковые моменты, которые, чаще всего, запланированы.
  • +24

Комментарии (59)

0
Полезно. Как вариант можно и Java-сервлетик достаточно просто написать и развернуть на Jetty.
Как хранилище сейчас тренд NoSql решения. Например, Apache Cassandra интересное решение.
Какие нагрузки удается держать?
Как мониторите сервер?
Хотелось бы больше услышать про организацию игровой логики в социалках. Например делаете ли очередь сообщений? Как часто синхроните клиент с сервером? Кешируете ли сессии на сервере? И т.д.
0
Пока коменты не работали, ответил на вопросы прям в статье, в самом конце, под заголовком UPD1.
0
ах да, чето каменты поламались
+3
Если это коммент опубликуется, то значит удалось починить.
+1
Работает!
+1
0
Где ты их берёшь?
+7
D:\Documents and Settings\Admin\Мои документы\Downloads\gif
0
в левом нижнем углу даже ссылка :D
0
Это был больше риторический возглас, выражающий возмущение…
+2
Эта еще не самая фиговая…
0
Наконец все заработало.
Плюс!
+1
Я что хотел написать-то…
Это конечно все хорошо, но нужно учитывать и минусы.
MSSQL — платный и дорогой sql-сервер, при том, что большинство его фич останутся невостребованными. Поэтому лучше сразу выбрать другую, бесплатную БД, например FireBird там какой-нибудь.
ASP.NET тоже обойдется дороже, т.к. Windows-хостинг обычно обходится дороже, поэтому лучше уж Java.
0
Немного не соглашусь.
Express Edition полностью бесплатен, и ограничен лишь на работу с одним физическим процессором. Даже ограничения на размер БД убрали (не помню правда совсем убрали или как-то сильно увеличели, но мои бд по >8 ГБайт вполне комфортно работают).
Насчет того что Win-хостинг дороже тоже не всегда правда, если брать VDS или выделенный сервер, то да, за лицензию windows придется заплатить.
Но, я выбрал windows в виду нежелания разбираться с linux серверами, да и администрировать windows мне не привыкать.
0
Забыл добавить: и программировать под Windows для меня в разы проще, нежели под linux.
Конечно, если в Java, например, есть большой опыт — то можно и linux заюзать.
все имхо
0
10 Гб вроде бы на одну базу на офф сайте.
Ну я к тому, что непрофильная конфигурация получается. Можно конечно использовать, если очень уж лень изучать другие решения.
+2
Самый главный минус такого подхода в поиске/стоимости специалистов.
Игры с асинхронным геймплеем замечательно пишутся и на LAMP.
0
да уж, найти enterprise-специалиста с уклоном в геймдев будет довольно проблематично :)
0
Для высоконагруженных проектов выбор LAMP изначально не есть правильная архитектура.
Реляционная модель данных (MySQL) мало походит для масштабирования. Есть кластерные базы данных NoSQL-типа. Cassandra проектировалась для кластера. Она умеет сама реплицировать данные по серверам, балансировать нагрузку между ними. У нее нет одной точки отказа. Она оптимизирована для записи.
По серверной части. Php – это скрипт. Запустился, выдал результат и умер. В java в большинстве случаев, мы будем работать с приложением. То есть в памяти на сервере висит приложение. Оно может хранить все сессионные данные активного пользователя (подняло из базы), реагировать на запросы клиента, и периодически сохранять данные. Синхронизацию java-серверов тоже можно организовать прозрачно и в памяти.
Не будем разводить холивар что быстрее php и или java. Это разные решения.
Со скриптами (php) проще с деплойментом серверной части. В java ж нужно обновлять целое приложение, а не отдельный скрипт.
0
Да, 99% геймпеля асинхронных игр — это получить данные игрока, изменить и сохранить их. Для этого удобнее использовать NoSQL БД. Но удобство это не настолько критичное. И специалистов меньше, они дороже.

MySQL, имеет возможности для масштабирования, ее используют крупные ребята (Вконтакте, LiveJournal и т.д.). Само собой никто не запрещает ставить кэш перед БД.

«Запустился, выдал результат и умер.» — снижает порог входа в технологию. Про сессии не понял. Можно хранить их как угодно — на диске, в памяти, в БД.

«Не будем разводить холивар что быстрее php и или java.» — И не нужно. 90% торомозов web-приложения — это взаимодействие с БД. Упереться в производительность языка очень не просто.
0
геймпеля -> геймплея
торомозов -> тормозов
0
MySQL, имеет возможности для масштабирования, ее используют крупные ребята (Вконтакте, LiveJournal и т.д.). Само собой никто не запрещает ставить кэш перед БД.
Масштабируется, но не так хорошо как Cassandra — практически линейно, путем добавления ноды в кластер. А большие ребята уже выросли и не могут изменить архитектуру. Создавая они проект сейчас, не факт что бы пошли по тому же пути.
0
Да.
В принципе для «получить данные игрока, изменить и сохранить их» можно использовать любую БД, под которую есть специалисты.

Я рассуждаю, как менеджер, который хочет максимально эффективно распределить бюджет. LAMP — дешево и сердито, легко искать спецов. Но есть и другие решения.
0
LAMP вообще-то:
P = Python
P = Perl
P = PHP
0
лучше уж MSSQL чем FireBird. MSSQL очень наворочен в плане управления базой. меньше хлопот будет.
0
Зачем для игр вообще реляционные БД?
0
я за NoSQL ;)
0
Спасибо за статью. А стоит ли браться за социалку нубу в серверных делах? Как сделать серверную часть на LAMP я знаю, но неужели нужно покупать VDS под это дело? Shared не покатит? :)
0
Если что-то серьезное и популярное — обычный Shared не покатит.
1) канал нужен выделенный от 50Мбит (иначе рискуете завалить сервер даже запросами иконок для приложения во время рекламы, у меня такое на одноклассниках было, пришлось залить статические ресурсы на другой сервер)
2) диски нужны под каждую БД отдельные, а то и по нескольку штук на каждую
3) для реализации автономной логики чаще требуется какой-нибудь демон/сервис или что-то подобное
Пишу из личного опыта, попробовал очень много всяких вариантов, ничего лучше выделенного не нашел. Облочные серверы всякие можно попробовать, хотя я люблю точно знать где и что у меня лежит и на чем выполняется.
+5
Если что-то серьезное и популярное — обычный Shared не покатит.
1) канал нужен выделенный от 50Мбит (иначе рискуете завалить сервер даже запросами иконок для приложения во время рекламы, у меня такое на одноклассниках было, пришлось залить статические ресурсы на другой сервер)
2) диски нужны под каждую БД отдельные, а то и по нескольку штук на каждую
3) для реализации автономной логики чаще требуется какой-нибудь демон/сервис или что-то подобное
Пишу из личного опыта, попробовал очень много всяких вариантов, ничего лучше выделенного не нашел. Облочные серверы всякие можно попробовать, хотя я люблю точно знать где и что у меня лежит и на чем выполняется.
Когда я делал первую социалку я был полный нуб в MSSQL, правда Oracle очень хорошо знал.
Не вижу препятствий для устремленного к цели разуму! Дерзайте!
+1
1) Сервер тогда очень странно себя вел, загрузить игру можно было только со второй третьей попытки, половина пакетов терялась, если игра запустилась. Чтобы избавиться от этой проблемы мы тогда залили картинки на Mail.ru в дополнительные файлы приложения, из других социалок нормально оттуда файлы забирались. Так что мотайте на ус. В одноклассниках вообще очень хорошо рекламируются приложения в первые дни (бесплатно). Но и попасть туда сложнее, и без круглосуточных работников лучше не пытаться даже.
+1
вместо json удобнее гонять готовые объекты по AMF, используя FluorineFX или WebORB — в последнем есть удобный менеджер подключений к БД, кодогенерация классов для клиенской и серверной стороны + неплохая консоль для тестирования и отслеживания траифка

оч. рекомендую :)
+1
А тут такой набегаю я и рекомендую Python + Google App Engine.

Потому что можешь выдерживать пиковые нагрузки и почти всё за тебя уже написали авторы платформы.

Пожалуй, напишу позже в блоги.
  • tdz
  • tdz
0
тоже интересно, обязательно напишите, ждем
0
фиг знает. Мой трекинг сервис на python+GAE пару раз показывал выход за пределы квот, причем игра была отнюдь не самая популярная и тречилось достаточно мало инфы. Если делать на нем настоящий сервер, я думаю будут проблемы.
+1
Лучше заплатить, чем уйти в даун обычным серваком, по-моему.
0
Проблема только в том, что можно заплатить очень много…
А так да, лучше заплатить…
0
Разве что кривой код сделать и дёргать сервер каждые 5 секунд:
www.google.com/enterprise/cloud/appengine/pricing.html

И вряд ли больше, чем ежемесячная плата за сервер, чьи мощности ты будешь использовать меньше.
0
Проблема в том, что сегодня гугл запускает сервис, завтра отключает. Зачем подстраивать архитектуру конкретно под GAE (где кроме плюсов есть и минусы), когда облачный рынок так стремительно развивается?
0
Ерунда, GAE с нами надолго.
0
И никто не мешает написать приложение, которое легко вынести GAE. Фреймворки, которые на нём используются работают и в других местах. Поменять придётся только запросы в другой тип БД.
0
Ага… кривой код… наивный )))
«Добрые люди» напишут скриптик который будет дергать твой сервак 100500 раз в сек…
И выйдет твой «не кривой код» тебе в несколько косарей зелени в месяц… И действительно не больше чем месячная плата за сервер )))

Проблема сервисов типа GAE для игр, это не защищенность от денежного ддоса. Т.е. он выдержит почти любую нагрузку, но вот радости у владельца может быть не очень много, после взгляда на счет в конце месяца. И проблема тут еще и в том, что такую бяку можно не замечать долго. Обычный сервак просто ляжет. Это убытки.
Но убытки вполне конкретные. А вот сколько тебе смогут «накрутить» пока ты не узнал об этом, это вопрос открытый.
Это все касается простейших серверов типа описанного в статье. Они наиболее подвержены таким атакам.
0
Кому ж ты нужен? Кто из нас наивный?
Also поставь счётчик и при превышении лимита убивай, если так параноишь по тысячным счетам от врагов геймдева.
+1
Если ты никому не нужен, то зачем тебе иггровой сервер, да еще и с прицелом на облако для обслуживания большого количества пользователей?
Поставить счетчик на что? На пользователя? При превышении лимита отключать его?

Наивный ты, раз пишешь ерунду типа «по тысячным счетам от врагов геймдева»… видать не поднял ни одного публичного сервиса в жизни.

P.S. Для справки. Подавляющее большинство игр, в которые играют конечно, пытаются похачить тем или иным способом. Или просто сделать какую нибудь бяку. Просто так. Вандалов в инете полно. Не говоря уже о грязной конкуренции.
0
Если ты никому не нужен, то зачем тебе иггровой сервер, да еще и с прицелом на облако для обслуживания большого количества пользователей?
Что за нелогичное предложение? У тебя паранойя же. Не тащи её к нам )

Поставить счетчик на что? На пользователя? При превышении лимита отключать его?
Да, если пользователь один шлёт запросов как десять, очевидно же, что пора его дропать до выяснения причин.

Наивный ты, раз пишешь ерунду типа «по тысячным счетам от врагов геймдева»… видать не поднял ни одного публичного сервиса в жизни.
Вот лично не поднимал, это делал серверный программист. Но никому вообще не сдалось их сервера ддосить, несмотря на посещаемость.
+2
Что за нелогичное предложение?
Для меня вполне логично, что человек говоря о каких-то вещах опирается на свой опыт. Ты говоришь о ненужности, соответственно я понял, что ты никому не нужен… и соответственно собственные ощущения переносишь на других. )))

Да, если пользователь один шлёт запросов как десять, очевидно же, что пора его дропать до выяснения причин.
Опять наивность и непонимание того, как оно происходит…

Вот лично не поднимал, это делал серверный программист.
Это опять говорит о совершенном не понимании сути вопроса.

Но никому вообще не сдалось их сервера ддосить, несмотря на посещаемость.
И о чем это говорит? Например, что они нафиг не уперлись даже вандалам-скрипткидисам… или же что они настолько некомпетентны, что не знают о том, что их сервисы «ковыряют» все кому не лень ))
В любом случае вариантов явно больше одного, для того чтобы так безапелляционно заявлять «Вандалов и черной конкуренции в инете нет!!! Это сказки для лохов!!!»…
+1
Поддерживаю, если вдруг у вас игра хотябы с > 300к пользователей и вас даже не пытались ддосить или ломать, значит вы либо логи не смотрите, либо вас уже давно сломали и как-то скрытно юзают.
Ваш сервер всегда нужен кому-нибудь и это даже не говоря о том, что саму игру будут в день по 5 раз пытаться сломать. PS: это я из опыта по играм для вконтакте/одноклассники.
0
Ну и нашёл очевидное:
If you believe your application has been the target of a denial of service attack, please contact our support team and include a snippet of your request logs that corroborates this, along with an explanation of why this activity indicates an attack. You may be issued a credit for the service for this period of time.
+1
Ну-ну. Удачи вам достучаться в «support team» гугла… )))
Или что-то им реально доказать. Это болото.
Хотя может у вас карма на них настроена…
0
)))))))))))
Тебя там на деньги опустили или где пруфы проблем реальных?

Биллинг саппорт гугла вполне адекватный. Купи премиум — будет ещё и инженерный.
0
У тебя интернет только что появился чтоли?
Зайди на форум той же вспышки и почитай как гугл кидает на деньги людей, без объяснения причин.

P.S. И да, меня они тоже кинули на деньги без объяснения причины. Хоть сумма и мелкая, но я рад, так легко отделался. У других это были тысячи баксов…
0
Дай ссылки, форум давно не читаю, в нём спид и хвостоколы.
+2
пишут, что не всегда прокатывает
habrahabr.ru/post/139312/
0
Не вижу ничего криминального по ссылке.
0
Also, мне надоело. Мне GAE нравится и я буду продолжать его юзать, вам — нет и вы не будете. Как-то так.
0
Опять странная логика…
Мне GAE нравится, как раз делаю на нем пробный проект.
С чего ты взял, что я его не буду юзать-то?
0
И выйдет твой «не кривой код» тебе в несколько косарей зелени в месяц…
Это ж копейки по меркам социалки. Один раз попал на несколько косарей, потом сделал заплатку. Ну ерунда же.
0
Поделись, как попал, если не тайна )
0
Я не о себе.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.