كود لعبة Command & Conquer: البق من التسعينيات. المجلد الثاني

image1.png


أصدرت الشركة الأمريكية Electronic Arts Inc (EA) شفرة المصدر المفتوح للألعاب Command & Conquer: Tiberian Dawn و Command & Conquer: Red Alert. تم العثور على عشرات الأخطاء في التعليمات البرمجية المصدر باستخدام محلل PVS-Studio ، لذا يرجى الترحيب باستمرار وصف العيوب التي تم العثور عليها.



المقدمة



Command & Conquer هي سلسلة من ألعاب الكمبيوتر من النوع الإستراتيجي في الوقت الحقيقي. تم إصدار أول لعبة في السلسلة في عام 1995. تم نشر الكود المصدري للألعاب مع إصدار مجموعة Command & Conquer Remastered .



للعثور على أخطاء في الكود ، تم استخدام محلل PVS-Studio . إنها أداة لاكتشاف الأخطاء ونقاط الضعف المحتملة في الكود المصدري للبرامج المكتوبة بلغة C و C ++ و C # و Java.



رابط إلى ملخص الخطأ الأول: " لعبة القيادة والقهر : البق من التسعينيات. المجلد الأول ".



أخطاء في الشروط



V583 عامل التشغيل '؟:' ، بصرف النظر عن تعبيره الشرطي ، يقوم دائمًا بإرجاع قيمة واحدة ونفس القيمة: 3072. STARTUP.CPP 1136



void Read_Setup_Options( RawFileClass *config_file )
{
  ....
  ScreenHeight = ini.Get_Bool("Options", "Resolution", false) ? 3072 : 3072;
  ....
}


اتضح أن المستخدمين لم يتمكنوا من التأثير على بعض الإعدادات. بتعبير أدق ، لقد فعلوا شيئًا ما ، ولكن نظرًا لحقيقة أن العامل الثلاثي يُرجع دائمًا قيمة واحدة ، في الواقع لم يتغير شيء.



V590 فكر في فحص تعبير 'i <8 && i <4'. التعبير مفرط أو يحتوي على خطأ مطبعي. DLLInterface.cpp 2238



// Maximum number of multi players possible.
#define MAX_PLAYERS 8 // max # of players we can have

for (int i = 0; i < MAX_PLAYERS && i < 4; i++) {
  if (GlyphxPlayerIDs[i] == player_id) {
    MultiplayerStartPositions[i] = XY_Cell(x, y);
  }
}


بسبب الدورة الخاطئة ، لم يتم تعيين المركز لجميع اللاعبين. من ناحية أخرى ، نرى العدد الثابت MAX_PLAYERS 8 ونفترض أن هذا هو الحد الأقصى لعدد اللاعبين. من ناحية أخرى، ونحن نرى حالة ط <4 و && المشغل . وبالتالي ، فإن الحلقة لا تُحدث 8 تكرارات أبدًا. على الأرجح ، في المرحلة الأولى من التطوير ، لم يستخدم المبرمج الثوابت ، وعندما بدأ ، نسي إزالة الأرقام القديمة من الكود.



V648 أولوية العملية "&&" أعلى من أولوية العملية "||" عملية. INFANTRY.CPP 1003



void InfantryClass::Assign_Target(TARGET target)
{
  ....
  if (building && building->Class->IsCaptureable &&
    (GameToPlay != GAME_NORMAL || *building != STRUCT_EYE && Scenario < 13)) {
    Assign_Destination(target);
  }
  ....
}


يمكنك جعل الكود غير واضح (وعلى الأرجح خاطئ) ببساطة عن طريق عدم تحديد أولوية العمليات لـ || عوامل التشغيل. و && . من غير الواضح تمامًا هنا ما إذا كان هذا خطأ أم لا. ولكن بالنظر إلى الجودة الإجمالية لرمز هذه المشاريع ، دعنا نفترض أنه هنا وفي عدة أماكن أخرى توجد أخطاء في أولوية العمليات:



  • V648 أولوية العملية "&&" أعلى من أولوية العملية "||" عملية. الفريق.CPP 456
  • V648 أولوية العملية "&&" أعلى من أولوية العملية "||" عملية. شاشة العرض CPP 1160
  • V648 أولوية العملية "&&" أعلى من أولوية العملية "||" عملية. عرض CPP 1571
  • V648 أولوية العملية "&&" أعلى من أولوية العملية "||" عملية. منزل.CPP 2594
  • V648 أولوية العملية "&&" أعلى من أولوية العملية "||" عملية. INIT.CPP 2541


V617 النظر في فحص الحالة. الوسيطة '((1L << STRUCT_CHRONOSPHERE))' الخاصة بـ '|' تحتوي العملية على مستوى البت على قيمة غير صفرية. منزل.CPP 5089



typedef enum StructType : char {
  STRUCT_NONE=-1,
  STRUCT_ADVANCED_TECH,
  STRUCT_IRON_CURTAIN,
  STRUCT_WEAP,
  STRUCT_CHRONOSPHERE, // 3
  ....
}

#define  STRUCTF_CHRONOSPHERE (1L << STRUCT_CHRONOSPHERE)

UrgencyType HouseClass::Check_Build_Power(void) const
{
  ....
  if (State == STATE_THREATENED || State == STATE_ATTACKED) {
    if (BScan | (STRUCTF_CHRONOSPHERE)) {  // <=
      urgency = URGENCY_HIGH;
    }
  }
  ....
}


للتحقق مما إذا تم تعيين بتات معينة في متغير ، استخدم عامل التشغيل & وليس |. نظرًا لوجود خطأ مطبعي في هذا الجزء من التعليمات البرمجية ، يكون الشرط دائمًا صحيحًا.



V768 يُستخدم ثابت التعداد "WWKEY_RLS_BIT" كمتغير من النوع المنطقي. لوحة المفاتيح CPP 286



typedef enum {
  WWKEY_SHIFT_BIT = 0x100,
  WWKEY_CTRL_BIT  = 0x200,
  WWKEY_ALT_BIT   = 0x400,
  WWKEY_RLS_BIT   = 0x800,
  WWKEY_VK_BIT    = 0x1000,
  WWKEY_DBL_BIT   = 0x2000,
  WWKEY_BTN_BIT   = 0x8000,
} WWKey_Type;

int WWKeyboardClass::To_ASCII(int key)
{
  if ( key && WWKEY_RLS_BIT)
    return(KN_NONE);
  return(key);
}


أعتقد ، في المعلمة الرئيسية ، أنهم أرادوا التحقق من جزء معين محدد بواسطة قناع WWKEY_RLS_BIT ، لكنهم ارتكبوا خطأ إملائيًا . يجب استخدام عامل التشغيل على مستوى البت & ، بدلاً من && ، للتحقق من رمز المفتاح.



تنسيق مريب



V523 عبارة "then" تعادل عبارة "else". RADAR.CPP 1827



void RadarClass::Player_Names(bool on)
{
  IsPlayerNames = on;
  IsToRedraw = true;
  if (on) {
    Flag_To_Redraw(true);
//    Flag_To_Redraw(false);
  } else {
    Flag_To_Redraw(true);   // force drawing of the plate
  }
}


ذات مرة ، علق أحد المطورين على التعليمات البرمجية لتصحيح الأخطاء. منذ ذلك الحين ، ظل الكود عاملًا شرطيًا مع نفس المشغلين في الفروع المختلفة.



تم العثور على نفس المكانين بالضبط:



  • V523 عبارة "then" تعادل عبارة "else". الخلية CPP 1792
  • V523 عبارة "then" تعادل عبارة "else". رادار CPP 2274


V705 من الممكن أنه تم نسيان كتلة "else" أو التعليق عليها ، وبالتالي تغيير منطق تشغيل البرنامج. NETDLG.CPP 1506



static int Net_Join_Dialog(void)
{
  ....
  /*...............................................................
  F4/SEND/'M' = edit a message
  ...............................................................*/
  if (Messages.Get_Edit_Buf()==NULL) {
    ....
  } else

  /*...............................................................
  If we're already editing a message and the user clicks on
  'Send', translate our input to a Return so Messages.Input() will
  work properly.
  ...............................................................*/
  if (input==(BUTTON_SEND | KN_BUTTON)) {
    input = KN_RETURN;
  }
  ....
}


نظرًا للتعليق الكبير ، لم يرى المطور العامل الشرطي غير المحدد أعلاه. تشكل بقية الكلمة الأساسية else كلمة أخرى إذا تم إنشاء الشرط أدناه ، والذي من المرجح أن يكون تغييرًا للمنطق الأصلي.



V519 يتم تعيين قيم لمتغير "ScoresPresent" مرتين على التوالي. ربما هذا خطأ. فحص السطور: 539 ، 541. INIT.CPP 541



bool Init_Game(int , char *[])
{
  ....
  ScoresPresent = false;
//if (CCFileClass("SCORES.MIX").Is_Available()) {
    ScoresPresent = true;
    if (!ScoreMix) {
      ScoreMix = new MixFileClass("SCORES.MIX");
      ThemeClass::Scan();
    }
//}


عيب محتمل آخر بسبب إعادة بناء ديون غير منتهية. الآن ليس من الواضح ما إذا كان يجب أن يكون متغير ScoresPresent صحيحًا أم لا يزال خاطئًا .



أخطاء إلغاء تخصيص الذاكرة



V611 تم تخصيص الذاكرة باستخدام عامل التشغيل "T [] الجديد ولكن تم تحريرها باستخدام عامل التشغيل" delete ". ضع في اعتبارك فحص هذا الرمز. ربما يكون من الأفضل استخدام "delete [] poke_data؛". CCDDE.CPP 410



BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type)
{
  ....
  char *poke_data = new char [length + 2*sizeof(int)]; // <=
  ....
  if(DDE_Class->Poke_Server( .... ) == FALSE) {
    CCDebugString("C&C95 - POKE failed!\n");
    DDE_Class->Close_Poke_Connection();
    delete poke_data;                                  // <=
    return (FALSE);
  }

  DDE_Class->Close_Poke_Connection();

  delete poke_data;                                    // <=

  return (TRUE);
}


اكتشف المحلل خطأً يتعلق بحقيقة أنه يمكن تخصيص الذاكرة وإصدارها بطرق غير متوافقة. لتحرير الذاكرة المخصصة للمصفوفة ، يجب أن تستخدم عامل الحذف [] وليس الحذف .



كان هناك العديد من هذه الأماكن ، وكلها تضر بالتطبيق الجاري (اللعبة) قيد التشغيل:



  • V611 تم تخصيص الذاكرة باستخدام عامل التشغيل "T [] الجديد ولكن تم تحريرها باستخدام عامل التشغيل" delete ". ضع في اعتبارك فحص هذا الرمز. ربما يكون من الأفضل استخدام "delete [] poke_data؛". CCDDE.CPP 416
  • V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] temp_buffer;'. INIT.CPP 1302
  • V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] progresspalette;'. MAPSEL.CPP 795
  • V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] grey2palette;'. MAPSEL.CPP 796
  • V611 تم تخصيص الذاكرة باستخدام عامل التشغيل "T [] الجديد ولكن تم تحريرها باستخدام عامل التشغيل" delete ". ضع في اعتبارك فحص هذا الرمز. ربما يكون من الأفضل استخدام "delete [] poke_data؛". CCDDE.CPP 422
  • V611 تم تخصيص الذاكرة باستخدام عامل التشغيل "T [] الجديد ولكن تم تحريرها باستخدام عامل التشغيل" delete ". ضع في اعتبارك فحص هذا الرمز. ربما يكون من الأفضل استخدام "delete [] temp_buffer؛". INIT.CPP 1139


V772 استدعاء عامل التشغيل "حذف" لمؤشر الفراغ سيؤدي إلى سلوك غير معرف. ENDING.CPP 254



void GDI_Ending(void)
{
  ....
  void * localpal = Load_Alloc_Data(CCFileClass("SATSEL.PAL"));
  ....
  delete [] localpal;
  ....
}


و الحذف و [] الحذف ومشغلي فصل لسبب ما. يقومون بعمل مختلف لتنظيف الذاكرة. وعند استخدام مؤشر غير من النوع ، لا يعرف المترجم نوع البيانات الذي يشير إليه المؤشر. في معيار لغة C ++ ، يكون سلوك المترجم غير محدد.



تم العثور أيضًا على عدد من تحذيرات المحلل من هذا النوع:



  • V772 استدعاء عامل التشغيل "حذف" لمؤشر الفراغ سيؤدي إلى سلوك غير معرف. HEAP.CPP 284
  • V772 استدعاء عامل التشغيل "حذف" لمؤشر الفراغ سيؤدي إلى سلوك غير معرف. INIT.CPP 728
  • V772 استدعاء عامل التشغيل "حذف" لمؤشر الفراغ سيؤدي إلى سلوك غير معرف. MIXFILE.CPP 134
  • V772 استدعاء عامل التشغيل "حذف" لمؤشر الفراغ سيؤدي إلى سلوك غير معرف. 391 مشروع محلول بلاستيك 391
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. MSGBOX.CPP 423
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. SOUNDDLG.CPP 407
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. BUFFER.CPP 126
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. BUFF.CPP 162
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. BUFF.CPP 212
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. BFIOFILE.CPP 330
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. EVENT.CPP 934
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. HEAP.CPP 318
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. INIT.CPP 3851
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. MIXFILE.CPP 130
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. MIXFILE.CPP 430
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. MIXFILE.CPP 447
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. MIXFILE.CPP 481
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. MSGBOX.CPP 461
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. QUEUE.CPP 2982
  • V772 Calling a 'delete' operator for a void pointer will cause undefined behavior. QUEUE.CPP 3167
  • V772 استدعاء عامل التشغيل "حذف" لمؤشر الفراغ سيؤدي إلى سلوك غير معرف. 406 مشروع محل تجريب 406


V773 تم الخروج من الوظيفة بدون تحرير مؤشر "لوحة التقدم". من الممكن حدوث تسرب للذاكرة. MAPSEL.CPP 258



void Map_Selection(void)
{
  ....
  unsigned char *grey2palette    = new unsigned char[768];
  unsigned char *progresspalette = new unsigned char[768];
  ....
  scenario = Scenario + ((house == HOUSE_GOOD) ? 0 : 14);
  if (house == HOUSE_GOOD) {
    lastscenario = (Scenario == 14);
    if (Scenario == 15) return;
  } else {
    lastscenario = (Scenario == 12);
    if (Scenario == 13) return;
  }
  ....
}


"إذا لم تقم بتحرير الذاكرة على الإطلاق ، فلن أكون مخطئًا بالتأكيد في اختيار مشغل!" - ربما فكر المبرمج.



image2.png


ولكن بعد ذلك يحدث تسرب للذاكرة ، وهو أيضًا خطأ. في مكان ما في نهاية الوظيفة ، يتم تحرير الذاكرة ، ولكن قبل ذلك هناك العديد من الأماكن التي يحدث فيها الخروج الشرطي من الوظيفة ، ولا يتم تحرير الذاكرة بواسطة المؤشرات grey2palette و Progresspalett .



منوعات



V570 تم تعيين المتغير "hdr-> MagicNumber" لنفسه. COMBUF.CPP 806



struct CommHdr {
  unsigned short MagicNumber;
  unsigned char Code;
  unsigned long PacketID;
} *hdr;

void CommBufferClass::Mono_Debug_Print(int refresh)
{
  ....
  hdr = (CommHdr *)SendQueue[i].Buffer;
  hdr->MagicNumber = hdr->MagicNumber;
  hdr->Code = hdr->Code;
  ....
}


يتم تهيئة حقلين من بنية CommHdr بقيمهما الخاصة. في رأيي ، عملية لا طائل من ورائها ، لكنها تجرى عدة مرات:



  • V570 يتم تخصيص المتغير "hdr-> Code" لنفسه. COMBUF.CPP 807
  • V570 تم تعيين المتغير "hdr-> MagicNumber" لنفسه. COMBUF.CPP 931
  • V570 يتم تخصيص المتغير "hdr-> Code" لنفسه. COMBUF.CPP 932
  • V570 تم تعيين المتغير "hdr-> MagicNumber" لنفسه. COMBUF.CPP 987
  • V570 يتم تخصيص المتغير "hdr-> Code" لنفسه. COMBUF.CPP 988
  • V570 يتم تخصيص المتغير "obj" لنفسه. الخريطة CPP 1132
  • V570 تم تعيين المتغير "hdr-> MagicNumber" لنفسه. COMBUF.CPP 910
  • V570 يتم تخصيص المتغير "hdr-> Code" لنفسه. COMBUF.CPP 911
  • V570 تم تعيين المتغير "hdr-> MagicNumber" لنفسه. COMBUF.CPP 1040
  • V570 يتم تخصيص المتغير "hdr-> Code" لنفسه. COMBUF.CPP 1041
  • V570 تم تعيين المتغير "hdr-> MagicNumber" لنفسه. COMBUF.CPP 1104
  • V570 يتم تخصيص المتغير "hdr-> Code" لنفسه. COMBUF.CPP 1105
  • V570 يتم تخصيص المتغير "obj" لنفسه. خريطة CPP 1279


V591 يجب أن تقوم الدالة غير الفارغة بإرجاع قيمة. HEAP.H 123



int FixedHeapClass::Free(void * pointer);

template<class T>
class TFixedHeapClass : public FixedHeapClass
{
  ....
  virtual int Free(T * pointer) {FixedHeapClass::Free(pointer);};
};


وظائف فئة Free TFixedHeapClass no بيان إرجاع المشغل . الشيء الأكثر إثارة للاهتمام هو أن الوظيفة المسماة FixedHeapClass :: Free لها قيمة إرجاع من النوع int أيضًا . على الأرجح ، نسي المبرمج ببساطة كتابة بيان الإرجاع والآن تُرجع الدالة قيمة غير مفهومة.



V672 ربما ليست هناك حاجة لإنشاء متغير "الضرر" الجديد هنا. تمتلك إحدى وسيطات الوظيفة نفس الاسم وهذه الوسيطة هي مرجع. فحص السطور: 1219 ، 1278. BUILDING.CPP 1278



ResultType BuildingClass::Take_Damage(int & damage, ....)
{
  ....
  if (tech && tech->IsActive && ....) {
    int damage = 500;
    tech->Take_Damage(damage, 0, WARHEAD_AP, source, forced);
  }
  ....
}


يتم تمرير معلمة الضرر عن طريق المرجع. لذلك ، من المتوقع حدوث تغييرات في قيم هذا المتغير في جسم الوظيفة. ولكن في مكان واحد ، أعلن المطور عن متغير بنفس الاسم. لهذا السبب ، سيتم تخزين القيمة 500 في تلف المتغير المحلي ، بدلاً من معلمة الوظيفة. ربما كان المقصود سلوك مختلف.



مكان آخر من هذا القبيل:



  • V672 ربما لا توجد حاجة لإنشاء متغير "الضرر" الجديد هنا. تمتلك إحدى وسيطات الوظيفة نفس الاسم وهذه الوسيطة هي مرجع. فحص الخطوط: 4031 ، 4068. TECHNO.CPP 4068


V762 من الممكن أن وظيفة افتراضية تم تجاوزها بشكل غير صحيح. انظر الوسيطة الأولى للدالة 'Occupy_List' في الفئة المشتقة 'BulletClass' والفئة الأساسية 'ObjectClass'. رصاصة ح 90



class ObjectClass : public AbstractClass
{
  ....
  virtual short const * Occupy_List(bool placement=false) const; // <=
  virtual short const * Overlap_List(void) const;
  ....
};

class BulletClass : public ObjectClass,
                    public FlyClass,
                    public FuseClass
{
  ....
  virtual short const * Occupy_List(void) const;                 // <=
  virtual short const * Overlap_List(void) const {return Occupy_List();};
  ....
};


اكتشف المحلل خطأً محتملاً أثناء إعادة تعريف الوظيفة الافتراضية Occupy_List . يمكن أن يؤدي هذا إلى استدعاء وظائف خاطئة في وقت التشغيل.



عدد قليل من الأماكن المشبوهة:



  • V762 من الممكن أن وظيفة افتراضية تم تجاوزها بشكل غير صحيح. شاهد مؤهلات الوظيفة "Ok_To_Move" في الفئة المشتقة "TurretClass" والفئة الأساسية "DriveClass". TURRET.H 76
  • V762 من الممكن أن وظيفة افتراضية تم تجاوزها بشكل غير صحيح. انظر الوسيطة الرابعة للدالة 'Help_Text' في الفئة المشتقة 'HelpClass' والفئة الأساسية 'DisplayClass'. المساعدة ح 55
  • V762 من الممكن أن وظيفة افتراضية تم تجاوزها بشكل غير صحيح. انظر الوسيطة الأولى للدالة "Draw_It" في الفئة المشتقة "MapEditClass" والفئة الأساسية "HelpClass". MAPEDIT.H 187
  • V762 It is possible a virtual function was overridden incorrectly. See first argument of function 'Occupy_List' in derived class 'AnimClass' and base class 'ObjectClass'. ANIM.H 80
  • V762 It is possible a virtual function was overridden incorrectly. See first argument of function 'Overlap_List' in derived class 'BulletClass' and base class 'ObjectClass'. BULLET.H 102
  • V762 It is possible a virtual function was overridden incorrectly. See qualifiers of function 'Remap_Table' in derived class 'BuildingClass' and base class 'TechnoClass'. BUILDING.H 281
  • V762 It is possible a virtual function was overridden incorrectly. See fourth argument of function 'Help_Text' in derived class 'HelpClass' and base class 'DisplayClass'. HELP.H 58
  • V762 من الممكن أن وظيفة افتراضية تم تجاوزها بشكل غير صحيح. انظر الوسيطة الأولى للدالة "Overlap_List" في الفئة المشتقة "AnimClass" والفئة الأساسية "ObjectClass". 90- مشروع صيد السمك


V763 يتم دائمًا إعادة كتابة المعامل "تنسيق" في جسم الوظيفة قبل استخدامه. DISPLAY.CPP 4031



void DisplayClass::Set_Tactical_Position(COORDINATE coord)
{
  int xx = 0;
  int yy = 0;

  Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight,
    Cell_To_Lepton(MapCellWidth) + GlyphXClientSidebarWidthInLeptons,
    Cell_To_Lepton(MapCellHeight));

  coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(....));

  if (ScenarioInit) {
    TacticalCoord = coord;
  }
  DesiredTacticalCoord = coord;
  IsToRedraw = true;
  Flag_To_Redraw(false);
}


تتم الكتابة فوق معلمة التنسيق على الفور في نص الوظيفة. لم يتم استخدام القيمة القديمة. إنه أمر مريب للغاية عندما يكون للوظيفة حجج ولا تعتمد عليها. ثم هناك بعض الإحداثيات.



وهذا المكان يستحق الزيارة:



  • V763 يتم دائمًا إعادة كتابة المعامل "تنسيق" في جسم الوظيفة قبل استخدامه. DISPLAY.CPP 4251


V507 يتم تخزين مؤشر المصفوفة المحلية "localpalette" خارج نطاق هذه المصفوفة. سيصبح هذا المؤشر غير صالح. MAPSEL.CPP 757



extern "C" unsigned char *InterpolationPalette;

void Map_Selection(void)
{
  unsigned char localpalette[768];
  ....
  InterpolationPalette = localpalette;
  ....
}


هناك الكثير من المتغيرات العالمية في كود اللعبة. ربما كان هذا نهجًا شائعًا للترميز في تلك الأيام. لكنها تعتبر الآن سيئة بل وخطيرة.



يتم تخزين لوحة المصفوفة المحلية في مؤشر InterpolationPalette ، والذي سيصبح غير صالح بعد الخروج من الوظيفة.



مكانان أكثر خطورة:



  • V507 يتم تخزين مؤشر المصفوفة المحلية "localpalette" خارج نطاق هذه المصفوفة. سيصبح هذا المؤشر غير صالح. MAPSEL.CPP 769
  • يتم تخزين مؤشر V507 إلى "المخزن المؤقت" للصفيف المحلي خارج نطاق هذا الصفيف. سيصبح هذا المؤشر غير صالح. WINDOWS.CPP 458


خاتمة



كما كتبت في التقرير الأول ، دعونا نأمل أن تكون المشاريع الجديدة لـ Electronic Arts ذات جودة أفضل. بشكل عام ، يقوم مطورو الألعاب بشراء PVS-Studio بنشاط . أصبحت ميزانيات الألعاب الآن كبيرة جدًا ، لذا لا يحتاج أحد إلى تكاليف إضافية لإصلاح الأخطاء في الإنتاج. كما أن إصلاح الخطأ في مرحلة مبكرة من الترميز عمليًا لا يستغرق وقتًا وموارد أخرى.



ندعوك لتنزيل وتجريب PVS-Studio في جميع المشاريع على موقعنا .





إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فيرجى استخدام رابط الترجمة: Svyatoslav Razmyslov. رمز لعبة القيادة والقهر: البق من التسعينيات. المجلد الثاني .



All Articles