الدفعة الرابعة / الدفعة الخامسة / أوامر خادم CM —— تقرير بحث صعوبة النقل
موضوع البحث: قراءة متعمقة لكود مصدر citymania-org/cmclient (فرع vanilla 15.3) تاريخ البحث: 2026-08-14 الاستنتاج: صعوبة العمل الثلاثة مختلفة جداً، مع تقييم كل عنصر والترتيب الموصى به.
أولاً: الدفعة الرابعة: تقسيم المدن (بما في ذلك حفظ growth_tiles)
مكونات الوظيفة
| المكون | الملف | الحجم | الوصف |
|---|---|---|---|
| تعداد وضع التقسيم | cm_zoning.hpp | 37 سطراً | 12 وضع تقييم (CHECKOPINION/CHECKBUILD/CHECKSTACATCH/CHECKACTIVESTATIONS/CHECKBULUNSER/CHECKINDUNSER/CHECKTOWNZONES/CHECKCBACCEPTANCE/CHECKCBTOWNLIMIT/CHECKTOWNADZONES/CHECKTOWNGROWTHTILES) |
| منطق التقييم | cm_zoning_cmd.cpp | 413 سطراً | دالة استعلام لكل وضع (ترجع SpriteID لوحة الألوان)، تتضمن خوارزمية نصف قطر TownZone/Tz، فحص تغطية StationFinder، فحص المباني/الصناعات غير المخدومة |
| واجهة مستخدم شريط أدوات التقسيم | cm_zoning_gui.cpp | 204 سطراً | نافذة اختيار وضع التقسيم للحلقة الداخلية/الخارجية (حالة عامة _zoning.inner/outer) |
| حفظ growth_tiles | cm_saveload.cpp/.hpp | 90 سطراً | حقول Town الموسعة growth_tiles + growth_tiles_last_month (std::map<TileIndex, uint8_t>)، مخزنة باستخدام معالج SaveLoad vanilla |
| تمديد البيانات | extensions/cmext_town.hpp | ~80 سطراً | ext::Town: growth_tiles ×2 + حقول كثيرة خاصة بأسلوب لعب خادم CM (إحصائيات بضائع CBTownInfo، تتبع الإعلانات/الأموال، عدد نمو المدن hs/cs/hr) |
| التدوير الشهري + التشغيل | cm_game.cpp / town_cmd.cpp | — | تدوير growth_tiles شهرياً؛ تسجيل الحالة عند بناء/هدم/إعادة بناء المنازل |
تقييم صعوبات النقل لكل عنصر
| الصعوبة | الخطورة | الوصف |
|---|---|---|
| إعادة كتابة نظام الحفظ | 🟠 متوسط | يستخدم growth_tiles DefaultSaveLoadHandler + SlSetStructListLength/SlObject الخاص بـ vanilla، jrpm يستخدم نظام sl/ الجديد (SlTableHeader/SlObjectSaveFiltered). يجب إعادة الكتابة باستخدام NSL/SLE + بوابة ميزة XSLF (إضافة XSLFI_TOWN_GROWTH_TILES، الإصدار 1)، الحفظ القديم لا يتأثر |
| تمديد هيكل Town | 🟢 منخفض | أخذ خريطتي growth_tiles فقط (تخطي CBTownInfo/حقول الإعلانات/الأموال——تلك خاصة بأسلوب لعب CityBuilder في خادم CM)؛ إضافة حقل إلى jrpm Town + ربط جدول حفظ town_sl.cpp |
| منطق التقييم | 🟢 منخفض-متوسط | معظمها استعلامات خالصة (GetTileType/StationFinder/Town cache/فحص المباني)، واجهة برمجة تطبيقات jrpm موجودة؛ خوارزمية نصف قطر TownZone (squared_town_zone_radius) تحتاج إلى مطابقة اسم حقل jrpm |
| خط أنابيب العرض | 🟠 متوسط-مرتفع | يحتاج DrawTileZoning إلى التعليق في خط أنابيب رسم viewport——يشترك مع تمييز الدفعة الثالثة في آلية عرض TileHighlight. بدون خط أنابيب تمييز، يجب بناؤه بشكل منفصل (يوصى بـ التمييز أولاً، ثم التقسيم) |
| موارد السبرايت | 🟠 متوسط | 12 لوحة ألوان تستخدم سبرايتات مخصصة CM_SPR_PALETTE_ZONING_*، jrpm لا يمتلكها ← يجب استخدام سبرايتات لوحة ألوان موجودة بدلاً منها أو إضافة موارد جديدة |
| خطافات الأحداث | 🟠 متوسط | يعتمد تسجيل growth_tiles على ناقل الأحداث الخاص بـ cmclient (event::HouseBuilt/HouseCleared/...، Emit في cm_main.cpp). jrpm لا يحتوي على هذه الآلية ← إضافة خطافات مباشرة في town_cmd.cpp عند بناء/هدم المنازل + تدوير شهري (IntervalTimer) |
استنتاج الصعوبة: 🟠 متوسط-مرتفع (حوالي 1.5–2 جولة خاصة، كل جولة 4–6 ساعات)
- المتطلب الأساسي: يوصى بشدة بإكمال نظام التمييز للدفعة الثالثة أولاً (خط أنابيب العرض مشترك)
- إذا تم فقط "تلوين 12 وضع تقسيم، بدون حفظ growth_tiles": تنخفض الصعوبة إلى 🟡 متوسط (توفير تمديد الحفظ، حوالي جولة واحدة)
ثانياً: الدفعة الخامسة: إعادة تشغيل الأوامر + التصدير/التسجيل
2.1 إعادة تشغيل الأوامر (cm_command_log + cm_commands + generated)
| المكون | الحجم | الوصف |
|---|---|---|
| طبقة كائن الأوامر | cm_command_type.hpp + generated/cm_gen_commands (2251+1418 سطراً) | كل أمر vanilla يُغلف ككائن قابل للبرمجة (as_company/with_callback/set_auto)، يتضمن تسلسل الأمر ← تدفق بتات |
| تحميل سجل الأوامر | cm_command_log.cpp (203 سطراً) | فك ضغط lzma + تحليل تدفق بتات BitOStream ← قائمة انتظار _fake_commands (عداد tick/نتيجة متوقعة/بذرة عشوائية/CommandPacket) |
| تنفيذ الأوامر | ExecuteFakeCommands | تنفيذ حسب ترتيب عداد tick: ExecuteCommand(&cp) (واجهة برمجة تطبيقات داخلية vanilla) + التحقق من البذرة العشوائية/النتيجة (مكافحة الغش)، عند الاتصال عبر الشبكة، يُعاد توجيهها لجميع العملاء |
الاختلافات الرئيسية (تحدد الصعوبة):
| cmclient | jrpm | التأثير |
|---|---|---|
ExecuteCommand(CommandPacket*) | لا توجد هذه الدالة، نواة الأمر هي DoCommandPInternal(Commands, TileIndex, CommandPayloadBase&, ...) | يحتاج إلى كتابة طبقة تحويل CommandPacket ← DoCommandPInternal لـ payload 🟠 |
| حقول CommandPacket (vanilla) | CommandPacket موجود لكن هيكله مختلف (GeneralCommandPacket<DynBaseCommandContainer>) | يحتاج إلى تكييف تعيين الحقول 🟠 |
GetCommandName | ✅ موجودة (command_func.h:166) | 🟢 |
outgoing_queue (إعادة التوجيه عبر الشبكة) | طبقة شبكة jrpm مختلفة (OutgoingCommandPacket/ServerNetworkGameSocketHandler::SendCommand) | إعادة تشغيل عبر الشبكة تحتاج إلى إعادة كتابة منطق إعادة التوجيه 🟠 |
| المسجل (كيفية توليد ملف .cmd) | خطاف التسجيل في cmclient يعتمد على اعتراض post() لطبقة كائن الأوامر | أوامر jrpm هي Post مقولبة ← يحتاج إلى إضافة خطاف تسجيل في نقطة توزيع الأوامر 🔴 العمل الأساسي |
| تنسيق الملف | خاص (معرف أمر vanilla + تدفق بتات) | معرف أمر jrpm يختلف تماماً عن vanilla ← ملفات إعادة التشغيل غير متوافقة، يحتاج التنسيق إلى إعادة تصميم 🟠 |
2.2 التصدير / التسجيل (cm_export.cpp، 536 سطراً)
| الوظيفة | الوصف | الصعوبة |
|---|---|---|
| ExportOpenttdData | تصدير JSON لمواصفات المنازل/مواصفات البضائع/لوحة الألوان/معلومات المحرك (JsonWriter) | 🟢 منخفض-متوسط (مستقل، قراءة هيكل Spec وكتابة JSON) |
| ViewportExport / ExportFrameSprites | تصدير سبرايتات viewport إطاراً بإطار (يعتمد على متجهات الرسم الداخلي لـ viewport TileSpriteToDrawVector/ParentSpriteToSortVector) | 🟠 متوسط-مرتفع (خط أنابيب viewport في jgrpp مختلف، يحتاج إلى محاذاة واجهة المتجه) |
استنتاج الصعوبة: 🟠 متوسط-مرتفع (حوالي جولتين خاصتين)
- إعادة تشغيل الأوامر هي الأكبر: العمل الأساسي = ① إضافة خطاف تسجيل في نقطة توزيع الأوامر (تكييف نظام الأوامر المقولب في jrpm) ② طبقة تنفيذ CommandPacket → DoCommandPInternal ③ تصميم تنسيق ملف جديد. يوصى بنقل طبقة كائن الأوامر أولاً (أساس مخططات الدفعة الثالثة، وأيضاً أساس إعادة التشغيل)
- التصدير: تصدير بيانات JSON يمكن القيام به بشكل مستقل أولاً (منخفض-متوسط)؛ تسجيل الإطارات يعتمد على محاذاة viewport (نفس دفعة التمييز)
ثالثاً: أوامر خادم CM (cm_console_cmds.cpp، 289 سطراً)
قائمة الأوامر وتقييم الصعوبة لكل عنصر
| الأمر | الوظيفة | الصعوبة على jrpm | ملاحظات |
|---|---|---|---|
cmgamespeed [n] | تغيير سرعة اللعبة | 🟢 منخفض جداً (~20 سطراً) | jrpm لديه بالفعل _game_speed العام (gfx.cpp:52)، فقط يحتاج إلى غلاف أمر |
cmstep [n] | تقدم n tick | ⏭️ jrpm لديه بالفعل | أمر step (ConStepGame) له نفس الوظيفة، تخطي |
cmexport | تصدير openttd.json | 🟢 منخفض-متوسط | يعتمد على ExportOpenttdData (انظر الدفعة الخامسة) |
cmtreemap <file> | زراعة أشجار حسب خريطة الارتفاع | 🟡 متوسط | قراءة خريطة الارتفاع + أمر زراعة الأشجار، مستقل |
cmreset_town_growth | مسح سجل نمو المدينة | 🟢 منخفض | يعتمد على حقل تقسيم المدن |
cmload_commands | تحميل إعادة تشغيل الأوامر | 🟠 متوسط-مرتفع | يعتمد على البنية التحتية لإعادة تشغيل الأوامر |
cmstart_record / cmstop_record | تسجيل الإطارات | 🟠 متوسط-مرتفع | يعتمد على البنية التحتية للتسجيل |
cmgamestats | إحصائيات جلسة اللعبة | 🟢 منخفض | مستقل |
cmgfxdebug | تصحيح الرسومات | 🟢 منخفض | مستقل |
استنتاج الصعوبة: 🟢 إجمالي منخفض (أمر واحد 0.5–2 ساعة)
الترتيب الموصى به: cmgamespeed (10 دقائق) ← cmgamestats/cmgfxdebug (كل ~1 ساعة) ← cmexport (مع تصدير الدفعة الخامسة) ← cmtreemap (~2 ساعة) ← cmreset_town_growth (مع الدفعة الرابعة) ← cmload_commands/cmstart_record (مع إعادة تشغيل/تسجيل الدفعة الخامسة).
رابعاً: المسار العام الموصى به
① نظام التمييز (أساس الدفعة الثالثة، ~2-3 جولات) ← أساس عرض المخططات/التقسيم
② طبقة كائن الأوامر (أساس مخططات الدفعة الثالثة + إعادة تشغيل الدفعة الخامسة، ~1-2 جولة)
③ المخططات (~1 جولة)
④ تقسيم المدن (~1.5 جولة، يعتمد على ① خط أنابيب العرض؛ growth_tiles باستخدام بوابة XSLF)
⑤ إعادة تشغيل الأوامر + التصدير (~2 جولة، يعتمد على ②؛ يمكن تصدير JSON بشكل مستقل مسبقاً)
⑥ أوامر خادم CM (تُدرج متفرقة بين الدفعات، gamespeed/step يمكن القيام بهما فوراً)خامساً: العناصر التي يمكن تنفيذها فوراً بتكلفة منخفضة (لا تعتمد على أي مشروع كبير)
- أمر
cmgamespeed(~20 سطراً، تغيير_game_speed) stepموجود بالفعل ← لا حاجة للتنفيذcmgamestats/cmgfxdebug(كل ~1 ساعة، مستقل)- تصدير JSON لـ
cmexport(~2 ساعة، مستقل) cmtreemap(~2 ساعة، مستقل)
هذه العناصر الخمسة حوالي نصف يوم عمل، يمكن تنفيذها كـ "دفعة صفرية" أولاً، بالتوازي مع الدفعات الثالثة/الرابعة/الخامسة دون تعارض.