在本文中,我将讨论为什么,为什么以及如何开始在Pascal上创建站点:Delphi / FPC。“ Pascal网站”可能与以下内容相关:writeln('Content-type: text/html');
但是不,一切都更加有趣!但是,可以在GitHub上获得真实站点(几乎所有站点)的源代码。做什么的?
总的来说,我从来不是专业的网络开发人员-我制作游戏。游戏,尤其是在线游戏,需要网站。因此,碰巧我开始为我的游戏制作更多的网站。在Perl上使用CGI-在2000年代初期/中期很流行。一切都很好,直到出现问题为止。2013年,我们开始举办Spectromancer游戏的在线锦标赛,为此,我在该游戏的网站上创建了一个锦标赛页面,其中显示了与谁一起玩的人,最新结果等。在比赛开始时,玩家页面已更新,并且...未加载。人们按下F5键,进一步加剧了这个问题。事实证明,对于CGI脚本,每秒甚至有4-5个请求,作为一个单独的Perl进程运行,大大降低了服务器的速度,并且每秒超过10个请求使它完全不可访问。压力测试是在排练比赛期间进行的,这很不错:稍后,我已经将更新的静态页面用于比赛。为什么?
因此,当需要将这个站点制作成新游戏时,问题就出现了-什么?不能在Perl上使用Brake CGI。Perl中的FastCGI?我无法想象如何在Perl中编写和调试多线程程序,我在使用普通脚本时遇到了很多问题。Node.js?如果不是因为对JS的敌意,这可能是最好的选择。而且由于游戏本身及其服务器是用Pascal编写的(实际上是Delphi,但FPC也是不错的),所以产生了这个主意-我们应该用相同的语言来制作网站吗?这将简化与游戏服务器的集成。“尝试不是折磨!” 我想了,决定尝试一下。怎么样?
我选择了SimpleCGI(SCGI)作为接口:它比FastCGI简单一些,后者的优点对我来说无关紧要-不需要将后端分发到不同的服务器,所有的东西都在同一服务器上运行。因此,该任务归结为某种SCGI框架的开发,该框架处理来自服务器的请求并响应一些空白模板而生成HTML页面。结果就是这样的模块框架。它由以下部分组成:- 主循环:接受传入的连接,读取请求并将其放入队列进行处理。将对已处理请求的现成响应写入连接套接字并关闭它们。
- (N ): , . worker' — .
- : HTML- ( ) . .
- : ( CGI.pm Perl). , ..
Perl脚本的一个非常方便的功能是,对网站进行少量更改非常容易:只需编辑脚本代码即可。无需编译,部署。当然,pascal是一种编译后的语言,它不会那样工作,但是我仍然希望能够在不重新启动过程的情况下尽可能进行更改。因此,我试图使模板系统足够灵活。她那样工作。模板文件位于“ templates”文件夹中:它们在流程启动时加载,并且在更改时重新加载-这样,您可以更改动态内容而无需重新启动流程。每个文件可以具有一个或多个模板。它们一起形成模板的字典(或散列):{“ name”->“ value”}。这是模板的静态字典-所有请求通用,并且其内容保持不变(直到文件内容更改)。还有一个-动态字典,它为每个请求创建为空,并用例如数据库中的动态数据处理程序填充。结合静态和动态数据,形成最终结果。模板声明示例:#NEWSFEED_ITEM:
<div class=NewsHeader>
<a href='/$LANG_LC/forum/thread/$NEWS_ID'><IF_NEWS_PINNED>[TOP] </IF_NEWS_PINNED>$NEWS_DATE $NEWS_TITLE</a>
</div>
<div class=NewsText>$NEWS_TEXT
<div align=right>
<a href='/$LANG_LC/forum/thread/$NEWS_ID'>$COMMENTS</a>
</div>
</div>
这是一个静态模板,用于以名称NEWSFEED_ITEM的名称写在新闻提要中,其中包含其他几个模板,例如NEWS_TEXT-一个动态模板,其中包含从数据库下载的新闻文本。转换是将$ TEMPLATE_NAME形式的所有子字符串递归替换为该模板的值。在这里,您还会注意到用于条件翻译的伪标记:<IF_TEMPLATE_NAME>-在翻译期间,根据指定模板的值,将删除此类标记,并保留或删除其内容。我特别选择了这种格式的条件-以HTML标记的形式,以便在文本编辑器中进行编辑时,语法突出显示有效并且很容易看到配对的标记。使用此模板的提要生成代码如下所示:
result:='';
// NEWSFEED_ITEM result
for i:=0 to n-1 do begin
id:=StrToIntDef(sa[i*c],0);
title:=sa[i*c+1];
cnt:=StrToIntDef(sa[i*c+2],1)-1;
flags:=StrToIntDef(sa[i*c+3],0);
//
db.Query('SELECT msg,created FROM messages WHERE topic=%d ORDER BY id LIMIT 1',
[id]);
if db.lastErrorCode<>0 then continue;
text:=db.Next;
date:=db.NextDate;
// ( temp)
temp.Put('NEWS_ID',id,true);
temp.Put('NEWS_DATE',FormatDate(date,true),true);
temp.Put('NEWS_TITLE',title,true);
temp.Put('NEWS_PINNED',flags and 4>0,true);
comLink:='$LNK_READ_MORE | ';
if cnt>0 then comLink:=comLink+inttostr(cnt)+' $LNK_COMMENTS'
else comLink:=comLink+'$LNK_LEAVE_COMMENT';
temp.Put('NEWS_TEXT',text,true);
temp.Put('COMMENTS',comLink,true);
//
result:=result+BuildTemplate('#NEWSFEED_ITEM');
end;
本土化
模板也方便用于本地化。为此,请使用全局(在请求的上下文中)clientLang变量。它的工作原理是这样的:如果请求处理程序发现客户端需要俄语页面,则他在clientLang中写入值“ RU”,然后模板翻译器在文本中找到$ TEMPLATE_NAME,始终尝试首先应用$ TEMPLATE_NAME_RU。因此,对于本地化,仅需要每个带有文本的模板为另一种语言创建其版本:#TITLE_NEWS:News
#TITLE_NEWS_RU:
使用框架的例子
简单的站点代码示例:program website;
uses SysUtils, SCGI;
//
function IndexPage:AnsiString; stdcall;
begin
result:=FormatHeaders('text/html')+BuildTemplate('#INDEX.HTM');
end;
begin
SetCurrentDir(ExtractFileDir(ParamStr(0)));
SCGI.Initialize; //
AddHandler('/',IndexPage); // '/'
SCGI.RunServer; //
end.
总
我在2015年底创建真实网站astralheroes.com的过程中编写了描述的框架。通常情况下,第一个煎饼有点块状-代码变得有些混乱和混乱,下一个站点正在变得更好。但是,我对过程和结果感到满意:该站点运行良好,易于调试和更新。发现:- 我期望与紧凑的Perl相比,站点代码非常膨胀,但是没有-用Pascal编写的相同功能仅需要Perl的两倍。但是看起来更清楚了。
- ! Perl — , - 100 , , . - - — . Delphi .
- Perl. -, , , . -, Perl, , .
- : , , . .
- . , , ( ), , . , , — . .
, . — . , :

, — . , .
?
GitHub上的源代码:github.com/Cooler2/ApusEngineExamples注意,存储库中有一个子模块,因此最好使用“ --recursive ” 参数进行克隆。该站点项目位于文件中:“ AH-Website \ Backend \ src \ website.dpr”这不是当前站点的完整副本:很明显,我无法使用播放器的数据发布数据库的内容,我也不会发布CGI脚本,因为它们与主题无关。但是,该项目正在组装,启动和工作中,充分展示了框架的工作。由于我在Patreon上获得的支持,使得站点代码及其使用的引擎代码的发布成为可能。。我感谢所有支持我并敦促您加入的人们-未来还有很多有趣的事情:)谢谢您的关注!