diff --git a/.github/workflows/pr-doc-checker.yml b/.github/workflows/pr-doc-checker.yml index 90a22d82ef..4b7e86e567 100644 --- a/.github/workflows/pr-doc-checker.yml +++ b/.github/workflows/pr-doc-checker.yml @@ -77,6 +77,7 @@ jobs: -e ^docs/Fixed-or-Improved-Logics.md$ \ -e ^docs/AI-Scripting-and-Mapping.md$ \ -e ^docs/User-Interface.md$ \ + -e ^docs/Interoperability.md$ \ -e ^docs/Miscellanous.md$ \ ) then diff --git a/.gitignore b/.gitignore index c7875aa55b..874d93ae3c 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ out # Python virtual environment for docs .venv/ + +debug.log diff --git a/CREDITS.md b/CREDITS.md index 99727ed0e0..3a86547c6f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -737,6 +737,7 @@ This page lists all the individual contributions to the project by their author. - Miners back to work when ore regenerated - Allow disable an over-optimization in targeting - Extra threat + - Export interface for external call - **solar-III (凤九歌)** - Target scanning delay customization (documentation) - Skip target scanning function calling for unarmed technos (documentation) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index d45a6ac91f..f49a3600ed 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -30,6 +30,11 @@ + + + + + @@ -221,6 +226,11 @@ + + + + + diff --git a/README.md b/README.md index 52c77fe94d..e56af57c5f 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,10 @@ Credits This project was founded by [@Belonit](https://github.com/Belonit) (Gluk-v48) and [@Metadorius](https://github.com/Metadorius) (Kerbiter) in 2020, with the first public stable release in 2021. Since then it has grown into a large community project with many contributors and maintainers. +### Interoperability + +Phobos has opened the external interfaces of some key components. If you are also developing your own engine extension and wish to use Phobos at the same time, please check out [Interoperability](Interoperability.md). + ### Maintenance crew Maintenance crew consists of experienced Phobos contributors who are recognized and given the permission to maintain and shape the project to the extent of their permissions. diff --git a/docs/General-Info.md b/docs/General-Info.md index 5b1dc16cde..85fabcc1ef 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -28,3 +28,12 @@ Phobos fully supports saving and loading thanks to prototype code from publicly While Phobos is standalone, it is designed to be used alongside [Ares](https://ares.strategy-x.com) and [CnCNet5 spawner](https://github.com/CnCNet/cncnet-for-ares). Adding new features or improving existing ones is done with compatibility with those in mind. While we would also like to support HAres we can't guarantee compatibility with it due to the separation of it's userbase and developers from international community. We welcome any help on the matter though! + +### Interop API Compatibility + +Phobos exports a set of C/C++ interop functions for external tools and mod loaders (see [Interoperability](Interoperability.md#api-version-tracking)). + +- **Version discovery:** External code should call `GetInteropBuildNumber()` before relying on specific API features. +- **API lifecycle:** All exported APIs are labeled with their availability range [startBuild, endBuild) in the Interoperability documentation. +- **Deprecated APIs:** When an API is deprecated, its function stub remains but calling it triggers a fatal error with a clear message. This ensures broken integrations fail fast with actionable guidance. +- **Recommended usage:** Check the API availability table in [Interoperability](Interoperability.md#api-availability-table) to ensure your target API is supported in the Phobos build you're using. diff --git a/docs/Interoperability.md b/docs/Interoperability.md new file mode 100644 index 0000000000..98faa464fa --- /dev/null +++ b/docs/Interoperability.md @@ -0,0 +1,234 @@ + +# Interoperability + +This page documents the exported interfaces in [Interop](https://github.com/Phobos-developers/Phobos/tree/develop/src/Interop). + +## API Version Tracking + +Phobos tracks Interop API versions using build numbers. Each exported API is labeled with its availability range: +- **[startBuild, endBuild)**: API is available from build `startBuild` up to (but not including) build `endBuild`. +- **[startBuild, ∞)**: API is still active and has no planned removal date. + +### GetInteropBuildNumber + +```cpp +unsigned int GetInteropBuildNumber() +``` + +Returns the current Phobos Interop build number. Use this to detect which APIs are available. + +**Availability:** [48, ∞) + +### Deprecated API Handling + +When an API is deprecated, its function stub is retained but with a fatal error handler. The calling application will receive a descriptive error message and must stop execution. This ensures: +1. Broken links are immediately detected at runtime (not a crash). +2. Clear messaging guides developers to the replacement API. +3. Migration timeline is documented in the error message. + +**Example** (not currently in use): + +```cpp +// DEPRECATED: Removed after build 52. Use NewAPI instead. +// Calling this will trigger a fatal error with the message: +// "SomeOldAPI_Deprecated has been removed (was available until build 52). Please use NewAPI." +``` + +### API Availability Table + +| API | Signature | Availability | Notes | +|-----|-----------|---------------|-------| +| AE_Attach | `int AE_Attach(...)` | [48, ∞) | Still active | +| AE_Detach | `int AE_Detach(...)` | [48, ∞) | Still active | +| AE_DetachByGroups | `int AE_DetachByGroups(...)` | [48, ∞) | Still active | +| AE_TransferEffects | `void AE_TransferEffects(...)` | [48, ∞) | Still active | +| ConvertToType_Phobos | `bool ConvertToType_Phobos(...)` | [48, ∞) | Still active | +| Bullet_SetFirerOwner | `bool Bullet_SetFirerOwner(...)` | [48, ∞) | Still active | +| EventExt_AddEvent | `bool EventExt_AddEvent(...)` | [48, ∞) | Still active | + +## New type and entity + +### AttachEffect + +AttachEffect interop APIs allow external code to attach, detach, and transfer attach effects on units. + +#### AE_Attach + +```cpp +int AE_Attach( + TechnoClass* pTarget, + HouseClass* pInvokerHouse, + TechnoClass* pInvoker, + AbstractClass* pSource, + const char** effectTypeNames, + int typeCount, + int durationOverride, + int delay, + int initialDelay, + int recreationDelay +) +``` + +Attaches one or more AttachEffect types to the target. + +**Availability:** [48, ∞) + +- Parameters: + - pTarget: Target unit to receive effects. + - pInvokerHouse: Invoker house context. + - pInvoker: Invoker techno context. + - pSource: Optional source object context. + - effectTypeNames: Array of AttachEffect type names. + - typeCount: Number of entries in effectTypeNames. + - durationOverride: If non-zero, duration override is applied. + - delay: If >= 0, delay override is applied. + - initialDelay: If >= 0, initial delay override is applied. + - recreationDelay: If >= -1, recreation delay override is applied. +- Returns: + - > 0 on success (value returned by internal attach routine). + - 0 on failure. +- Fails when: + - pTarget is null. + - effectTypeNames is null. + - typeCount <= 0. + - No valid effect type name resolves to an existing AttachEffectType. + +#### AE_Detach + +```cpp +int AE_Detach( + TechnoClass* pTarget, + const char** effectTypeNames, + int typeCount +) +``` + +Detaches effects by explicit effect type names. + +- Parameters: + - pTarget: Target unit to remove effects from. + - effectTypeNames: Array of AttachEffect type names to remove. + - typeCount: Number of entries in effectTypeNames. +**Availability:** [48, ∞) +- Returns: + - > 0 on success (value returned by internal detach routine). + - 0 on failure. +- Fails when: + - pTarget is null. + - effectTypeNames is null. + - typeCount <= 0. + - No valid effect type name resolves to an existing AttachEffectType. + +#### AE_DetachByGroups + +```cpp +int AE_DetachByGroups( + TechnoClass* pTarget, + const char** groupNames, + int groupCount +) +``` + +Detaches effects by AttachEffect group name. + +- Parameters: + - pTarget: Target unit to remove effects from. + - groupNames: Array of group names. + - groupCount: Number of entries in groupNames. +**Availability:** [48, ∞) +- Returns: + - > 0 on success (value returned by internal detach-by-group routine). + - 0 on failure. +- Fails when: + - pTarget is null. + - groupNames is null. + - groupCount <= 0. + - groupNames contains no non-null entries. + +#### AE_TransferEffects + +```cpp +void AE_TransferEffects( + TechnoClass* pSource, + TechnoClass* pTarget +) +``` + +Transfers all attached effects from source to target. + +- Parameters: + - pSource: Source unit. + - pTarget: Target unit. + +**Availability:** [48, ∞) + +- Behavior: + - No operation if pSource or pTarget is null. + +## Vanilla class extension + +### TechnoExt + +#### ConvertToType_Phobos + +```cpp +bool ConvertToType_Phobos(FootClass* pThis, TechnoTypeClass* toType) +``` + +Converts a FootClass instance to another TechnoType. + +- Parameters: + - pThis: Unit to convert. + - toType: Destination TechnoType. + +**Availability:** [48, ∞) + +- Returns: + - true if conversion succeeds. + - false if conversion fails. +- Notes: + - This API forwards directly to TechnoExt::ConvertToType. + +### BulletExt + +#### Bullet_SetFirerOwner + +```cpp +bool Bullet_SetFirerOwner(BulletClass* pBullet, HouseClass* pHouse) +``` + +Updates the recorded firer house for a bullet extension. + +- Parameters: + - pBullet: Bullet instance. + - pHouse: New firer house (can be null if caller intentionally clears ownership). + +**Availability:** [48, ∞) + +- Returns: + - true if the bullet extension is found and updated. + - false on failure. +- Fails when: + - pBullet is null. + - No BulletExt entry exists for pBullet. + +### EventExt + +#### EventExt_AddEvent + +```cpp +bool EventExt_AddEvent(EventExt* pEventExt) +``` + +Invokes AddEvent on an EventExt object. + +- Parameters: + - pEventExt: Event extension instance. + +**Availability:** [48, ∞) + +- Returns: + - false if pEventExt is null. + - Otherwise, returns the result of pEventExt->AddEvent(). + + diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 23bf39f7e2..9a1358049e 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -691,6 +691,7 @@ Fixes / interactions with other extensions: - Fixed the initial direction of building placed by Ares's UnitDelivery superweapon (by NetsuNegi) - [Customize whether transport can kept or kill passengers when driver has been killed](New-or-Enhanced-Logics.md#customize-whether-transport-can-kept-or-kill-passengers-when-driver-has-been-killed) (by NetsuNegi) - Fixed a bug where passengers created by the InitialPayload logic or TeamType with `Full=true` would fail to fire when the transport unit with `OpenTopped=yes` moved to an area that the passengers' `MovementZone` cannot move into (by NetsuNegi) +- [Export interface for external call](index.md#interoperability) (by TaranDahl) ``` diff --git a/docs/index.md b/docs/index.md index 6fd2d8e666..45f870286e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,6 +16,7 @@ New / Enhanced Logics Fixed / Improved Logics AI Scripting and Mapping User Interface +Interoperability Miscellanous ``` diff --git a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po index 11ac79c191..854aae154e 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po +++ b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po @@ -2449,6 +2449,9 @@ msgstr "修复了非超时空矿车在矿区无矿后即便矿石已重新生成 msgid "Miners back to work when ore regenerated" msgstr "矿车在矿石再生后返回工作" +msgid "Export interface for external call" +msgstr "用于外部调用的导出接口" + msgid "**solar-III (凤九歌)**" msgstr "**solar-III(凤九歌)**" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index 5a38a291ca..bc96b13e28 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -2450,6 +2450,11 @@ msgstr "" "修复了 `OpenTopped=yes` 的运输工具移动到乘客 `MovementZone` 所不支持的区域时由初始载员逻辑或勾选了 " "`预装载小队` 的作战小队创建的乘客无法开火的 Bug(by NetsuNegi)" +msgid "" +"[Export interface for external call](index.md#interoperability) (by " +"TaranDahl)" +msgstr "用于外部调用的导出接口(by TaranDahl)" + msgid "0.4.0.3" msgstr "0.4.0.3" diff --git a/docs/locale/zh_CN/LC_MESSAGES/index.po b/docs/locale/zh_CN/LC_MESSAGES/index.po index a158b9f6f9..6b2bd6d582 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/index.po +++ b/docs/locale/zh_CN/LC_MESSAGES/index.po @@ -384,6 +384,17 @@ msgstr "" "[@Metadorius](https://github.com/Metadorius)(Kerbiter)于 2020 年创立,2021 " "年首次发布稳定版。此后它已发展成为一个由众多贡献者和维护者组成的大型社区项目。" +msgid "Interoperability" +msgstr "互操作性" + +msgid "" +"Phobos has opened the external interfaces of some key components. If you " +"are also developing your own engine extension and wish to use Phobos at " +"the same time, please check out [Interop](https://github.com/Phobos-" +"developers/Phobos/tree/develop/src/Interop)." +msgstr "Phobos为一些关键组件打开了外部接口。如果你也在开发自己的引擎扩展并且希望同时使用Phobos," +"请查阅[Interop](https://github.com/Phobos-developers/Phobos/tree/develop/src/Interop)。" + msgid "Maintenance crew" msgstr "维护团队" diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index bfc21c09d1..82b1d62b50 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index 4ffddf15c2..85ad897768 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "Interop/TechnoExt.h" // Cursor & target acquisition stuff not directly tied to other features can go here. @@ -506,6 +507,11 @@ DEFINE_HOOK(0x70CF87, TechnoClass_ThreatCoefficient_CanAttackMeThreatBonus, 0x9) }; ApplyLastTargetDistanceBonus(); + for (auto const& cb : TechnoExtInterop::CalculateExtraThreatCallbacks) + { + totalThreat = cb(pThis, pTarget, totalThreat); + } + return 0; } diff --git a/src/Interop/AttachEffect.cpp b/src/Interop/AttachEffect.cpp new file mode 100644 index 0000000000..681a1a96d7 --- /dev/null +++ b/src/Interop/AttachEffect.cpp @@ -0,0 +1,109 @@ + +#include "AttachEffect.h" +#include "New/Entity/AttachEffectClass.h" +#include "New/Type/AttachEffectTypeClass.h" + +DEFINE_EXPORT(int, AE_Attach, + TechnoClass* pTarget, + HouseClass* pInvokerHouse, + TechnoClass* pInvoker, + AbstractClass* pSource, + const char** effectTypeNames, + int typeCount, + int durationOverride, + int delay, + int initialDelay, + int recreationDelay +) +{ + if (!pTarget || !effectTypeNames || typeCount <= 0) + return 0; + + AEAttachInfoTypeClass attachInfo; + + for (int i = 0; i < typeCount; i++) + { + if (effectTypeNames[i]) + { + if (auto pType = AttachEffectTypeClass::Find(effectTypeNames[i])) + attachInfo.AttachTypes.push_back(pType); + } + } + + if (attachInfo.AttachTypes.empty()) + return 0; + + if (durationOverride != 0) + attachInfo.DurationOverrides.push_back(durationOverride); + + if (delay >= 0) + attachInfo.Delays.push_back(delay); + + if (initialDelay >= 0) + attachInfo.InitialDelays.push_back(initialDelay); + + if (recreationDelay >= -1) + attachInfo.RecreationDelays.push_back(recreationDelay); + + return AttachEffectClass::Attach(pTarget, pInvokerHouse, pInvoker, pSource, attachInfo); +} + +DEFINE_EXPORT(int, AE_Detach, + TechnoClass* pTarget, + const char** effectTypeNames, + int typeCount +) +{ + if (!pTarget || !effectTypeNames || typeCount <= 0) + return 0; + + AEAttachInfoTypeClass detachInfo; + + for (int i = 0; i < typeCount; i++) + { + if (effectTypeNames[i]) + { + if (auto pType = AttachEffectTypeClass::Find(effectTypeNames[i])) + detachInfo.RemoveTypes.push_back(pType); + } + } + + if (detachInfo.RemoveTypes.empty()) + return 0; + + return AttachEffectClass::Detach(pTarget, detachInfo); +} + +DEFINE_EXPORT(int, AE_DetachByGroups, + TechnoClass* pTarget, + const char** groupNames, + int groupCount +) +{ + if (!pTarget || !groupNames || groupCount <= 0) + return 0; + + AEAttachInfoTypeClass detachInfo; + + for (int i = 0; i < groupCount; i++) + { + if (groupNames[i]) + detachInfo.RemoveGroups.push_back(groupNames[i]); + } + + if (detachInfo.RemoveGroups.empty()) + return 0; + + return AttachEffectClass::DetachByGroups(pTarget, detachInfo); +} + +DEFINE_EXPORT(void, AE_TransferEffects, + TechnoClass* pSource, + TechnoClass* pTarget +) +{ + if (!pSource || !pTarget) + return; + + AttachEffectClass::TransferAttachedEffects(pSource, pTarget); +} diff --git a/src/Interop/AttachEffect.h b/src/Interop/AttachEffect.h new file mode 100644 index 0000000000..40c40a84f2 --- /dev/null +++ b/src/Interop/AttachEffect.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +// Interop exports for AttachEffect operations. +// C# P/Invoke examples (simplified): +// ```csharp +// [DllImport("Phobos.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "AE_Attach")] +// public static extern int AE_Attach(IntPtr pTarget, IntPtr pInvokerHouse, IntPtr pInvoker, IntPtr pSource, IntPtr effectTypeNames, int typeCount, int durationOverride, int delay, int initialDelay, int recreationDelay); +// +// [DllImport("Phobos.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "AE_Detach")] +// public static extern int AE_Detach(IntPtr pTarget, IntPtr effectTypeNames, int typeCount); +// ``` + +#include "Utilities/Macro.h" + +/// +/// Attaches AttachEffect instances to a target unit. +/// +DEFINE_EXPORT(int, AE_Attach, + TechnoClass* pTarget, + HouseClass* pInvokerHouse, + TechnoClass* pInvoker, + AbstractClass* pSource, + const char** effectTypeNames, + int typeCount, + int durationOverride, + int delay, + int initialDelay, + int recreationDelay +); + +/// +/// Removes AttachEffect instances matching given types from a unit. +/// +DEFINE_EXPORT(int, AE_Detach, + TechnoClass* pTarget, + const char** effectTypeNames, + int typeCount +); + +/// +/// Removes AttachEffect instances matching given groups from a unit. +/// +DEFINE_EXPORT(int, AE_DetachByGroups, + TechnoClass* pTarget, + const char** groupNames, + int groupCount +); + +/// +/// Transfers AttachEffect instances from one unit to another. +/// +DEFINE_EXPORT(void, AE_TransferEffects, + TechnoClass* pSource, + TechnoClass* pTarget +); diff --git a/src/Interop/BulletExt.cpp b/src/Interop/BulletExt.cpp new file mode 100644 index 0000000000..c73d6dac9a --- /dev/null +++ b/src/Interop/BulletExt.cpp @@ -0,0 +1,16 @@ + +#include "BulletExt.h" + +DEFINE_EXPORT(bool, Bullet_SetFirerOwner, BulletClass* pBullet, HouseClass* pHouse) +{ + if (!pBullet) + return false; + + const auto pBulletExt = BulletExt::ExtMap.TryFind(pBullet); + + if (!pBulletExt) + return false; + + pBulletExt->FirerHouse = pHouse; + return true; +} diff --git a/src/Interop/BulletExt.h b/src/Interop/BulletExt.h new file mode 100644 index 0000000000..3fe4a0130e --- /dev/null +++ b/src/Interop/BulletExt.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Utilities/Macro.h" +#include +#include + +DEFINE_EXPORT(bool, Bullet_SetFirerOwner, BulletClass* pBullet, HouseClass* pHouse); diff --git a/src/Interop/EventExt.cpp b/src/Interop/EventExt.cpp new file mode 100644 index 0000000000..61576116d9 --- /dev/null +++ b/src/Interop/EventExt.cpp @@ -0,0 +1,10 @@ +#include "EventExt.h" + +DEFINE_EXPORT(bool, EventExt_AddEvent, EventExt* pEventExt) +{ + if (pEventExt) + { + return pEventExt->AddEvent(); + } + return false; +} diff --git a/src/Interop/EventExt.h b/src/Interop/EventExt.h new file mode 100644 index 0000000000..d4c91c519c --- /dev/null +++ b/src/Interop/EventExt.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Utilities/Macro.h" +#include + +DEFINE_EXPORT(bool, EventExt_AddEvent, EventExt* pEventExt); diff --git a/src/Interop/TechnoExt.cpp b/src/Interop/TechnoExt.cpp new file mode 100644 index 0000000000..57d8856c13 --- /dev/null +++ b/src/Interop/TechnoExt.cpp @@ -0,0 +1,18 @@ +#include "TechnoExt.h" +#include +#include + +std::vector TechnoExtInterop::CalculateExtraThreatCallbacks = {}; + + + +DEFINE_EXPORT(bool, ConvertToType_Phobos, FootClass* pThis, TechnoTypeClass* toType) +{ + return TechnoExt::ConvertToType(pThis, toType); +} + +DEFINE_EXPORT(void, RegisterCalculateExtraThreatCallback, CalculateExtraThreatCallback callback) +{ + if (callback) + TechnoExtInterop::CalculateExtraThreatCallbacks.push_back(callback); +} diff --git a/src/Interop/TechnoExt.h b/src/Interop/TechnoExt.h new file mode 100644 index 0000000000..6cf6c3f24b --- /dev/null +++ b/src/Interop/TechnoExt.h @@ -0,0 +1,37 @@ +#pragma once + + +// Interop exports for external engine extensions. +// C# P/Invoke example: +// ```csharp +// [DllImport("Phobos.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "ConvertToType_Phobos")] +// [return: MarshalAs(UnmanagedType.I1)] +// public static extern bool ConvertToType_Phobos(IntPtr pThis, IntPtr toType); +// ``` +// Use `IntPtr` or `unsafe` pointers in managed code. The function below exposes +// the same signature as used internally (FootClass*, TechnoTypeClass*). + +#include "Utilities/Macro.h" +#include + +#include + +#include + +DEFINE_CALLBACK(int, CalculateExtraThreatCallback, TechnoClass* pThis, ObjectClass* pTarget, double originalThreat); + +class TechnoExtInterop +{ +public: + static std::vector CalculateExtraThreatCallbacks; +}; + +/// +/// Converts a unit to a different type. +/// +/// Pointer to the FootClass instance to convert +/// Pointer to the target TechnoTypeClass +/// true if conversion was successful, false otherwise +DEFINE_EXPORT(bool, ConvertToType_Phobos, FootClass* pThis, TechnoTypeClass* toType); + +DEFINE_EXPORT(void, RegisterCalculateExtraThreatCallback, CalculateExtraThreatCallback callback); diff --git a/src/Interop/Version.cpp b/src/Interop/Version.cpp new file mode 100644 index 0000000000..dd0b127ab1 --- /dev/null +++ b/src/Interop/Version.cpp @@ -0,0 +1,21 @@ +#include "Version.h" +#include "../Phobos.version.h" +#include "Utilities/Debug.h" + +DEFINE_EXPORT(unsigned int, GetInteropBuildNumber) +{ + return BUILD_NUMBER; +} + +// ============================================================================ +// Deprecated API implementation example (commented out for future use) +// ============================================================================ +/* +DEFINE_EXPORT(int, SomeOldAPI_Deprecated, int param1) +{ + // Trigger fatal error with explanation. + Debug::Fatal("SomeOldAPI_Deprecated has been removed (was available until build 52). " + "Please update your code to use the new API."); + return 0; // unreachable +} +*/ diff --git a/src/Interop/Version.h b/src/Interop/Version.h new file mode 100644 index 0000000000..1159935e09 --- /dev/null +++ b/src/Interop/Version.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Utilities/Macro.h" + +/// +/// Returns the current Phobos Interop build number. +/// Each exported API is annotated with availability range [startBuild, endBuild). +/// If endBuild == 0, the API is still active. +/// +DEFINE_EXPORT(unsigned int, GetInteropBuildNumber); + +// ============================================================================ +// Deprecated API example (commented out for future use) +// When an API reaches end-of-life, keep its function stub but call fatal error. +// ============================================================================ +/* +/// +/// DEPRECATED: Removed after build 52. Use SomeNewAPI instead. +/// Calling this function will trigger a fatal error. +/// +DEFINE_EXPORT(int, SomeOldAPI_Deprecated, + int param1 +); +*/ diff --git a/src/Utilities/Macro.h b/src/Utilities/Macro.h index a0bdefff07..b6801b0800 100644 --- a/src/Utilities/Macro.h +++ b/src/Utilities/Macro.h @@ -3,6 +3,19 @@ #include #include "Patch.h" +// Export / calling-convention macros for public C ABI functions +// +// Usage: +// DEFINE_EXPORT(return_type, Name, arglist...) +// Expands to: PHOBOS_EXPORT return_type PHOBOS_DECL Name(arglist) +#define DEFINE_EXPORT(ret, name, ...) extern "C" __declspec(dllexport) ret __stdcall name(__VA_ARGS__) + +// Callback typedef helper +// Usage: +// DEFINE_CALLBACK(return_type, TypeName, arglist...) +// Expands to: typedef return_type (__stdcall* TypeName)(arglist); +#define DEFINE_CALLBACK(ret, name, ...) typedef ret (__stdcall* name)(__VA_ARGS__) + #define GET_REGISTER_STATIC_TYPE(type, dst, reg) static type dst; _asm { mov dst, reg } template