重新启动用D编写的具有15年历史的游戏–第1部分编译

2020-12-20 14:04:11

最近在探索D时,我想起15年前在大学期间玩过的游戏。很长一段时间,我完全忘记了这个名字,只是这个名字来自日本的开发商。经过一番搜寻后,我终于找到了游戏的名称:Torus Trooper!

用D编写,这是我在忙于学习C ++时根本不了解的语言

有什么比尝试用现代版本的D编译D v0.110项目更好的项目,并且可以将游戏移植到WebAssembly + WebGL!所以我们在这里...

游戏使用Ant及其build.xml文件生成可执行文件,资源等。此后,D添加了DUB作为构建系统/程序包管理器,让我们来使用它!

{" authors" :["左健太" ,"雷米·吉利格(Remi Gillig)" ],"版权" :"版权©2004-Cho Kenta,2020-Remi Gillig" ,"说明" :" Torus Trooper(重新启动)" ,"许可证" :" BSD 2条款" ,"名称" :" tt" ," targetType" :"可执行文件" ," sourcePaths" :[" src" ]}

执行"调试"使用C:\ D \ dmd2 \ windows \ bin64 \ dmd.exe来构建x86.tt〜master:构建配置" application" ... src \ abagames \ tt \ barrage.d(95,33) :错误:使用D样式BulletMLParserTinyXML * [char []] [char [] parsersrc \ abagames \ tt \ barrage.d(122,28)代替C样式语法:错误:代替C样式语法,使用D样式BulletMLParserTinyXML * [] plsrc \ abagames \ tt \ barrage.d(130,14):错误:使用D样式BulletMLParserTinyXML * [char [] pasrc \ abagames \ tt \ boot .d(51,12):错误:使用D样式char [4096] exesrc \ abagames \ tt \ tunnel.d(300,14):错误:模板参数应在!src \ abagames之后,而不是C样式语法\ tt \ tunnel.d(322,14):错误:!src \ abagames \ util \ rand.d(115,6)之后应使用模板参数:错误:代替C样式语法,请使用D样式uint [N ] statesrc \ abagames \ util \ rand.d(140,20):错误:使用D风格的uint []代替C风格的语法,init_keysrc \ abagames \ util \ sdl \ luminous.d(21,10):错误:使用D样式GLuint [LUMINOUS_TEX代替C样式语法TURE_WIDTH_MAX * LUMINOUS_TEXTURE_HEIGHT_MAX * 4 *(uint).sizeof] tdsrc \ abagames \ util \ sdl \ luminous.d(83,17):错误:使用D样式float [2] [2] lmOfsC代替C样式语法:\ D \ dmd2 \ windows \ bin64 \ dmd.exe失败,退出代码为1。

这是一个易于解决的问题,来自编译器的建议有效,我只是用建议替换了此错误的每个实例。

@@ -42,7 +43,7 @@ struct SDL_AudioSpec {一旦回调返回,缓冲区将不再有效。立体声样本以LRLRLR顺序存储。 * /-void(* callback)(void * userdata,Uint8 * stream,int len); + void函数(void * userdata,Uint8 * stream,int len)回调; *用户数据; }

这是一个有趣的文件,这是此错误触发的文件之一:

私有void calcIndex(在float z中,在ind idx中,在float ofs外){idx = slice。长度+ 99999; for(int i = 1; i< slice.length; i ++){if((z< slice [i] .depth)){idx = i-1; ofs =(z-slice [idx]。depth)/(slice [idx + 1]。depth-slice [idx]。depth);休息;如果(idx< 0){idx = 0;的= 0; } if(idx> = slice。length-1){idx = slice。长度-2;的ofs = 0.99; } if(ofs!> = 0)//错误在这里ofs = 0;否则(ofs> = 1)ofs = 0.99; }

我确实尝试了几次搜索都没有成功,所以我在D社区论坛上问了一下,一些人猜对了,它当然不是> =,相当于<。但是,感谢Walter Bright,我从Digital Mars获得了原始文档的链接。与!> =的细微差别是,如果任何操作数为NaN,它也会返回true。建议的修复程序似乎有效:

D1以前将char []作为字符串类型,并在Phobos的std.string中起作用以操纵该类型。 D2用别名字符串= immutable(char)[]代替了它。结果,很多代码需要更新来处理。这包括自定义绑定中的代码。

@@ -92,24 +91,24 @@ public class Barrage {* / public class BarrageManager {private:-static BulletMLParserTinyXML * parser [char []] [char []]; -静态const char [] BARRAGE_DIR_NAME =" barrage&#34 ;; +静态BulletMLParserTinyXML * [string] [string]解析器; +静态常量字符串BARRAGE_DIR_NAME =" barrage&#34 ;;公共静态无效负载(){-char [] []目录= listdir(BARRAGE_DIR_NAME); -foreach(char [] dirName; dirs){-char [] [] files = listdir(BARRAGE_DIR_NAME〜" /"〜dirName); -foreach(char [] fileName; files){-if(getExt(fileName)!=" xml")+ string [] dirs = listdir(BARRAGE_DIR_NAME); + foreach(字符串dirName;目录){+字符串[]文件= listdir(BARRAGE_DIR_NAME〜" /"〜dirName); + foreach(字符串fileName;文件){+如果(fileName.extension!=" .xml")继续; parser [dirName] [fileName] = getInstance(dirName,fileName); }}}

D1以前具有std.string.toString来将值转换为char []值。此功能已替换为std.conv.to!string。

@@ -5,6 +5,7 @@ * /模块abagames.tt.shot; +私有导入标准转换私人进口标准件;私有导入std.string;私人进口opengl; @@ -179,7 +180,7 @@公共类镜头:演员{否则(sc> 2000)大小= 0.7;大小* =(1 +乘数* 0.01f); -fl.set(" X"〜std.string.toString(multiplier),pos,size * pos.y,+ fl.set(" X"〜to!string(multiplier ),pos,大小* pos.y,cast(int)(30 +乘数* 0.3f));} if(chargeShot){

将字符串传递给C的通常方法似乎只是使用std.string.toStringz。但是,在D1中,它曾经返回char *:

同样,修复非常简单。例如,绑定的编写并未考虑到这一点,因此需要对其进行修复:

@@ -81,7 +81,7 @@ struct SDL_RWops {/ *从各种数据源创建SDL_RWops结构的功能* / -SDL_RWops * SDL_RWFromFile(char * file,char * mode); + SDL_RWops * SDL_RWFromFile(const char * file,const char * mode);

@@ -28,10 +28,10 @@导入SDL_types; extern(C):-typedef int(* _seek_func_t)(SDL_RWops * context,int偏移量,int来源); -typedef int(* _read_func_t)(SDL_RWops * context,void * ptr,int size,int maxnum); -typedef int(* _write_func_t)(SDL_RWops * context,void * ptr,int size,int num); -typedef int(* _close_func_t)(SDL_RWops * context); + alias int function(SDL_RWops * context,int offset,int whence)_seek_func_t; + alias int函数(SDL_RWops * context,void * ptr,int size,int maxnum)_read_func_t; + alias int function(SDL_RWops * context,void * ptr,int size,int num)_write_func_t; + alias int函数(SDL_RWops * context)_close_func_t;

@@ -59,7 +59,7 @@公共类PrefData {public this(){gradeData = new GradeData [Ship.GRADE_NUM]; -foreach(inout GradeData gd; gradeData)+ foreach(ref GradeData gd; gradeData)gd =新的GradeData; }

自动T→自动:似乎原作者指定了auto以及不再允许的类型

@@ -631,9 +631,9 @@公共类BulletShape:Drawable {} private void createSquareShape(bool wireShape){-auto Vector3 cp = new Vector3; -自动Vector3 [] p =新Vector3 [4]; -自动Vector3 [] np =新Vector3 [4]; + auto cp =新的Vector3; + auto p =新的Vector3 [4]; + auto np =新的Vector3 [4];静态const float [] [] [] POINT_DAT = [[[-1,-1,1],[1,-1,1],[1,1,1],[-1,1,1],] ,[[-1,-1,-1],[1,-1,-1],[1、1,-1],[-1、1,-1],],@@ -642,9 +642,9 @@公共类BulletShape:可绘制{[[[1,-1,-1],[1,-1,1],[1,1,1],[1,1,-1],] ,[[-1,-1,-1],[-1,-1、1],[-1、1、1],[-1、1,-1],],]; -foreach(inout Vector3 ip; p)+ foreach(ref Vector3 ip; p)ip =新的Vector3; -foreach(inout Vector3 inp; np)+ foreach(ref Vector3 inp; np)inp =新Vector3; for(int i = 0; i< 6; i ++){cp.x = cp.y = cp.z = 0;

getExt→.extension:行为不同,但很容易修复,现在包含点

@@ -44,10 +45,10 @@公共类SoundManager:abagames.util.sdl.sound.SoundManager {私有静态Music [] loadMusics(){Music []音乐; -char [] []文件= listdir(Music.dir); -foreach(char []文件名;文件){-char [] ext = getExt(fileName); -if(ext!=" ogg"&& ext!=" wav")+ string [] files = listdir(Music.dir); + foreach(字符串fileName;文件){+字符串ext = fileName.extension; +如果(ext!=" .ogg"&& ext!=" .wav")继续;音乐=新音乐(); music.load(fileName);

catch(Object)→catch(Throwable):您可以先抛出任何Object,现在必须使用Throwable

@@ -83,20 +84,20 @@ public int boot(char [] [] args){}试试{mainLoop.loop(); -} catch(对象o){+} catch(Throwable t){试试{gameManager.saveErrorReplay(); -} catch(对象o1){}-抛出o; +} catch(Throwable){} +投掷t; }返回EXIT_SUCCESS; }

bit→SDL_bool:在D1中使用了位类型,它的行为像布尔值,但只使用了一位,因此您可以轻松地创建位字段,它主要用于SDL绑定中,因此我将其替换为SDL_bool

@@ -132,7 +133,7 @@ const uint SDL_SRCALPHA = 0x00010000; / * Blit使用源Alpha混合* / const uint SDL_PREALLOC = 0x01000000; / * Surface使用预先分配的内存* / / *如果需要在访问之前锁定表面,则评估为true * /-位SDL_MUSTLOCK(SDL_Surface * surface)+ SDL_bool SDL_MUSTLOCK(SDL_Surface * surface){返回surface.offset || ((surface.flags&(SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_RLEACCEL))!= 0); @@ -184,13 +185,7 @@ struct SDL_Overlay {void / * private_yuvhwdata * / * hwdata; / *特殊标志* /-联合-{-位hw_overlay; -Uint32 _dummy; -}-// Uint32 hw_overlay:1; / *标志:此叠加层硬件是否已加速? * /-// Uint32 UnusedBits:31; + Uint32标志; }

旧代码确实使用了std.stream,据我所知,不赞成使用std.stream,而是将其拆分为更具模块化的设计。但是,为了转换旧代码,有人抢救了已弃用的代码,并将其放入称为undead的DUB程序包中。

在开始编译代码时,这非常有用。但是,我想摆脱这种依赖关系,因为保留旧代码而不是依靠现代D等效项是不合适的。在D1中,std.stream.File.read可以将二进制数据读取为值。我将其更改为使用std.file.read和std.bitmanip.read。确保指定字节序也很重要,因为D1代码是针对实现的,而不是字节大小的类型。在Windows上是小端。这意味着重播和高分保存现在与原始可执行文件兼容!

@@ -5,7 +5,10 @@ * /模块abagames.tt.replay; -私有导入不死流; +导入std.file; +导入std.array; +导入std.bitmanip; +私人导入abagames.util.sdl.recordablepad; / ** @@ -22,28 +25,24 @@ public class ReplayData {private:public void save(string fileName){-auto fd = new File; -fd.create(dir〜" /"〜fileName); -fd.write(VERSION_NUM); -fd.write(级别); -fd.write(grade); -fd.write(种子); -padRecord.save(fd); -fd.close(); +自动缓冲区=追加程序!(ubyte []); + buffer.append!(int,Endian.littleEndian)(VERSION_NUM); + buffer.append!(float,Endian.littleEndian)(等级); + buffer.append!(int,Endian.littleEndian)(等级); + buffer.append!(long,Endian.littleEndian)(种子); + padRecord.save(buffer); + std.file.write(dir〜" /"〜fileName,buffer []); } public void load(string fileName){-auto fd = new File; -fd.open(dir〜" /"〜fileName); -int ver; -fd.read(ver); +自动缓冲区= cast(ubyte [])std.file.read(dir〜" /"〜fileName); + int ver = buffer.read!(int,Endian.littleEndian);如果(ver!= VERSION_NUM)抛出新错误("版本号错误"); -fd.read(级别); -fd.read(等级); -fd.read(种子); +级别= buffer.read!(float,Endian.littleEndian); +等级= buffer.read!(int,Endian.littleEndian); +种子= buffer.read!(long,Endian.littleEndian); padRecord =新的PadRecord; -padRecord.load(fd); -fd.close(); + padRecord.load(buffer); }}

代码库中有一个Vector类型,自然会使用运算符重载。但是,其语法已更改。

public void opAddAssign(向量v){x + = v。 X ; y + = v。 ; } public void opSubAssign(向量v){x-= v。 X ; y-= v。 ; } public void opMulAssign(float a){x * = a; y * = a; } public void opDivAssign(float a){x / = a; y / = a; }

public void opOpAssign(string op)(Vector v)if(op ==" +" || op =="-"){mixin(" x&#34 ;〜op〜" = vx;"); mixin(" y"〜op〜" = v.y;"); } public void opOpAssign(string op)(float a)if(op ==" *" || op ==" /"){mixin(" x&# 34;〜op〜" = a;"); mixin(" y"〜op〜" = a;"); }

该游戏还带有用于SDL,SDL_mixer,OpenGL和GLU的导入库,以及用于BulletML的静态库,BulletML是一个原始库,原始开发人员使用该库从XML加载项目符号模式。这些文件都是.lib文件,但我尝试使用的Windows SDK工具无法读取它们,lib或dumpbin无法读取它们。但是,幸运的是,dmd仍然支持这些文件,并且设法将它们链接。

所需的更改主要与外部代码的定义方式有关。 现在,由于我只是尝试生成Windows版本,因此无条件地将extern(Win32)(现在是version(Windows))的使用替换为extern(Windows)。 @@ -1,10 +1,4 @@ -version(Win32){-私人导入std.c.windows.windows; -extern(Windows):-} -version(linux){-extern(C):-} + extern(Windows):别名uint GLenum; 别名ubyte GLboolean; BulletML绑定使用extern(C),并且工作原理相同,因此未做任何更改。 有了这些,我已经设法编译了一个新的Windows可执行文件! 令我惊讶的是,几乎不需要进行任何语言更改。 最大的变化与Phobos和过时的库功能有关。