суббота, 20 сентября 2014 г.

Space Engineers документация по API

Статья носит ознакомительный характер, и не претендует на истину в последней инстанции.
По мере изучения API я буду пополнять информацию.

И так доступ к игровому миру и ее механике мы получаем через класс MyAPIGateway.
Который в себе содержит следующие  интерфейсы:

         IMyCubeBuilder CubeBuilder;
         IMyEntities Entities;
         IMyMultiplayer Multiplayer;
         IMyPlayerCollection Players;
         IMySession Session;
         IMyTerminalActionsHelper TerminalActionsHelper;
         IMyUtilities Utilities;

По скольку все они объявлены через модификатор static нам не нужно создавать экземпляр данного класса, мы просто будем обращаться напрямую.

Рассмотрим каждый.

1) CubeBuilder - содержит методы и объекты для управления процессов создания блоков.

 public interface IMyCubeBuilder
    {
        bool BlockCreationIsActivated { get; }
        bool CopyPasteIsActivated { get; }
        bool FreezeGizmo { get; set; }
        bool IsActivated { get; }
        bool ShipCreationIsActivated { get; }
        bool ShowRemoveGizmo { get; set; }
        bool UseSymmetry { get; set; }
        bool UseTransparency { get; set; }

        void Activate();
        void ActivateShipCreationClipboard(MyObjectBuilder_CubeGrid grid, VRageMath.Vector3 centerDeltaDirection, float dragVectorLength);
        void ActivateShipCreationClipboard(MyObjectBuilder_CubeGrid[] grids, VRageMath.Vector3 centerDeltaDirection, float dragVectorLength);
        bool AddConstruction(IMyEntity buildingEntity);
        void Deactivate();
        void DeactivateBlockCreation();
        void DeactivateCopyPaste();
        void DeactivateShipCreationClipboard();
        IMyCubeGrid FindClosestGrid();
        void StartNewGridPlacement(MyCubeSize cubeSize, bool isStatic);
    }

2) Entities - этот интерфейс я думаю будет интересней всего для создателей модов. Он содержит в себе методы для создания и управления всеми подконтрольными нам сущностями игрового мира, сенсоры, двигатели, орудия, кнопки и т.д.

    public interface IMyEntities
    {
        event Action OnCloseAll;
        event Action<IMyEntity, string, string> OnEntityNameSet;
        event Action<IMyEntity> OnEntityRemove;

        void AddEntity(IMyEntity entity, bool insertIntoScene = true);
        IMyEntity CreateFromObjectBuilder(MyObjectBuilder_EntityBase objectBuilder);
        IMyEntity CreateFromObjectBuilderAndAdd(MyObjectBuilder_EntityBase objectBuilder);
        IMyEntity CreateFromObjectBuilderNoinit(MyObjectBuilder_EntityBase objectBuilder);
        void EnableEntityBoundingBoxDraw(IMyEntity entity, bool enable, VRageMath.Vector4? color = null, float lineWidth = 0.01f, VRageMath.Vector3? inflateAmount = null);
        bool EntityExists(string name);
        bool Exist(IMyEntity entity);
        bool ExistsById(long entityId);
        VRageMath.Vector3? FindFreePlace(VRageMath.Vector3 basePos, float radius, int maxTestCount = 20, int testsPerDistance = 5, float stepSize = 1f);
        List<IMyEntity> GetElementsInBox(ref VRageMath.BoundingBox boundingBox);
        void GetEntities(HashSet<IMyEntity> entities, Func<IMyEntity, bool> collect = null);
        List<IMyEntity> GetEntitiesInAABB(ref VRageMath.BoundingBox boundingBox);
        List<IMyEntity> GetEntitiesInSphere(ref VRageMath.BoundingSphere boundingSphere);
        IMyEntity GetEntity(Func<IMyEntity, bool> match);
        IMyEntity GetEntityById(long entityId);
        IMyEntity GetEntityByName(string name);
        void GetInflatedPlayerBoundingBox(ref VRageMath.BoundingBox playerBox, float inflation);
        IMyEntity GetIntersectionWithSphere(ref VRageMath.BoundingSphere sphere);
        IMyEntity GetIntersectionWithSphere(ref VRageMath.BoundingSphere sphere, IMyEntity ignoreEntity0, IMyEntity ignoreEntity1);
        List<IMyEntity> GetIntersectionWithSphere(ref VRageMath.BoundingSphere sphere, IMyEntity ignoreEntity0, IMyEntity ignoreEntity1, bool ignoreVoxelMaps, bool volumetricTest);
        IMyEntity GetIntersectionWithSphere(ref VRageMath.BoundingSphere sphere, IMyEntity ignoreEntity0, IMyEntity ignoreEntity1, bool ignoreVoxelMaps, bool volumetricTest, bool excludeEntitiesWithDisabledPhysics = false, bool ignoreFloatingObjects = true, bool ignoreHandWeapons = true);
        bool IsInsideVoxel(VRageMath.Vector3 pos, VRageMath.Vector3 hintPosition, out VRageMath.Vector3 lastOutsidePos);
        bool IsInsideWorld(VRageMath.Vector3 pos);
        bool IsNameExists(IMyEntity entity, string name);
        bool IsRaycastBlocked(VRageMath.Vector3 pos, VRageMath.Vector3 target);
        bool IsSelectable(IMyEntity entity);
        bool IsSpherePenetrating(ref VRageMath.BoundingSphere bs);
        bool IsTypeHidden(Type type);
        bool IsTypeSelectable(Type type);
        bool IsVisible(IMyEntity entity);
        bool IsWorldLimited();
        void MarkForClose(IMyEntity entity);
        void RegisterForDraw(IMyEntity entity);
        void RegisterForUpdate(IMyEntity entity);
        void RemapObjectBuilder(MyObjectBuilder_EntityBase objectBuilder);
        void RemapObjectBuilderCollection(IEnumerable<MyObjectBuilder_EntityBase> objectBuilders);
        void RemoveEntity(IMyEntity entity);
        void RemoveFromClosedEntities(IMyEntity entity);
        void RemoveName(IMyEntity entity);
        void SetEntityName(IMyEntity IMyEntity, bool possibleRename = true);
        void SetTypeHidden(Type type, bool hidden);
        void SetTypeSelectable(Type type, bool selectable);
        bool TryGetEntityById(long id, out IMyEntity entity);
        bool TryGetEntityByName(string name, out IMyEntity entity);
        void UnhideAllTypes();
        void UnregisterForDraw(IMyEntity entity);
        void UnregisterForUpdate(IMyEntity entity, bool immediate = false);
        float WorldHalfExtent();
        float WorldSafeHalfExtent();
    }

3) Multiplayer - данный интерфейс предоставляет нам скудные возможности по получению информации о сетевой игре.

    public interface IMyMultiplayer
    {
        bool IsServer { get; }
        bool MultiplayerActive { get; }
        ulong MyId { get; }
        string MyName { get; }
        IMyPlayerCollection Players { get; }
        ulong ServerId { get; }

        bool IsServerPlayer(IMyPlayer player);
    }

4) Players - немаловажный интерфейс, благодаря которому скоро появятся NPC в игре стараниями моддеров. Предоставляет возможность добавлять, убивать, удалять игроков.

     public interface IMyPlayerCollection
    {
        DictionaryReader<long, MyPlayerInfo> AllPlayers { get; }
        int Count { get; }

        IMyPlayer this[ulong steamUserId] { get; }

        void Add(IMyPlayer player);
        long AddNewNpc(string npcName);
        bool Exists(ulong steamUserId);
        void ExtendControl(IMyEntity entityWithControl, IMyEntity entityGettingControl);
        IMyPlayer GetPlayerControllingEntity(IMyEntity entity);
        void GetPlayers(List<IMyPlayer> players, Func<IMyPlayer, bool> collect = null);
        bool HasExtendedControl(IMyEntity firstEntity, IMyEntity secondEntity);
        void KillPlayer(long playerId);
        void ReduceControl(IMyEntity entityWhichKeepsControl, IMyEntity entityWhichLoosesControl);
        void Remove(IMyPlayer player);
        void Remove(ulong steamUserId);
        void RemoveControlledEntity(IMyEntity entity);
        void RevivePlayer(long playerId);
        void SendControlledEntities(ulong sendTo);
        SerializableDictionary<long, ulong> Serialize();
        void SetControlledEntity(ulong steamUserId, IMyEntity entity);
        void TryExtendControl(IMyEntity entityWithControl, IMyEntity entityGettingControl);
        bool TryGetPlayer(ulong steamUserId, out IMyPlayer player);
        bool TryReduceControl(IMyEntity entityWhichKeepsControl, IMyEntity entityWhichLoosesControl);
        bool TrySetControlledEntity(ulong steamUserId, IMyEntity entity);
        void UpdateFromPlayer(long playerId);
        void UpdateFromPlayers();
        void UpdateFromSession(List<MyObjectBuilder_Checkpoint.PlayerItem> playersFromSession);
    }

5) Session - данный интерфейс позволяет работать с текущей игровой сессией и ее параметрами ( творческий ли режим, максимальное кол-во игроков и т.д.)

    public interface IMySession
    {
        float AssemblerEfficiencyMultiplier { get; }
        float AssemblerSpeedMultiplier { get; }
        bool AutoHealing { get; }
        uint AutoSaveInMinutes { get; }
        IMyCameraController CameraController { get; }
        bool CargoShipsEnabled { get; }
        bool ClientCanSave { get; }
        IMyControllableEntity ControlledObject { get; }
        bool CreativeMode { get; }
        string CurrentPath { get; }
        string Description { get; set; }
        TimeSpan ElapsedPlayTime { get; }
        bool EnableCopyPaste { get; }
        MyEnvironmentHostilityEnum EnvironmentHostility { get; }
        DateTime GameDateTime { get; set; }
        float GrinderSpeedMultiplier { get; }
        float HackSpeedMultiplier { get; }
        float InventoryMultiplier { get; }
        bool IsCameraAwaitingEntity { get; set; }
        short MaxFloatingObjects { get; }
        short MaxPlayers { get; }
        bool MultiplayerAlive { get; set; }
        bool MultiplayerDirect { get; set; }
        double MultiplayerLastMsg { get; set; }
        string Name { get; set; }
        float NegativeIntegrityTotal { get; set; }
        MyOnlineModeEnum OnlineMode { get; }
        string Password { get; set; }
        IMyLocalPlayer Player { get; }
        float PositiveIntegrityTotal { get; set; }
        float RefinerySpeedMultiplier { get; }
        bool ShowPlayerNamesOnHud { get; }
        bool SurvivalMode { get; }
        bool ThrusterDamage { get; }
        string ThumbPath { get; }
        TimeSpan TimeOnBigShip { get; }
        TimeSpan TimeOnFoot { get; }
        TimeSpan TimeOnJetpack { get; }
        TimeSpan TimeOnSmallShip { get; }
        IMyVoxelMaps VoxelMaps { get; }
        bool WeaponsEnabled { get; }
        float WelderSpeedMultiplier { get; }
        ulong? WorkshopId { get; }
        string WorldID { get; set; }

        void BeforeStartComponents();
        void Draw();
        void GameOver();
        void GameOver(MyTextsWrapperEnum? customMessage);
        MyObjectBuilder_Checkpoint GetCheckpoint(string saveName);
        MyObjectBuilder_Sector GetSector();
        Dictionary<string, byte[]> GetVoxelMapsArray();
        MyObjectBuilder_World GetWorld();
        bool IsPausable();
        void RegisterComponent(MySessionComponentBase component, MyUpdateOrder updateOrder, int priority);
        bool Save(string customSaveName = null);
        void SetAsNotReady();
        void Unload();
        void UnloadDataComponents();
        void UnloadMultiplayer();
        void UnregisterComponent(MySessionComponentBase component);
        void Update(float timeInSeconds);
        void UpdateComponents();
    }

6) TerminalActionsHelper - еще не разобрался, но думаю это как то связано с терминалами в игре..

    public interface IMyTerminalActionsHelper
    {
        void GetActions(Type blockType, List<ITerminalAction> resultList, Func<ITerminalAction, bool> collect = null);
    }

7) Utilities - интерфейс утилит, тут мы имеем доступ к чату, уведомлениям, и к неким хранилищам данных, мне еще предстоит разобраться с этим.

    public interface IMyUtilities
    {
        IMyConfigDedicated ConfigDedicated { get; }

        event MessageEnteredDel MessageEntered;

        string GetTypeName(Type type);
        TextReader ReadFileInGlobalStorage(string file);
        TextReader ReadFileInLocalStorage(string file, Type callingType);
        void SendMessage(string messageText);
        void ShowMessage(string sender, string messageText);
        void ShowNotification(string message, int disappearTimeMs = 2000, MyFontEnum font = MyFontEnum.White);
        TextWriter WriteFileInGlobalStorage(string file);
        TextWriter WriteFileInLocalStorage(string file, Type callingType);
    }


Space Engineers API C#

Я в кратце расскажу как писать собственные моды под игру Space Engineers на языке C# (Sharp).
Статья больше ориентирована на более менее опытных людей, знакомых с азами программирования на одном из объектно ориентированных языков.

В статье будет описаны лишь базовые вещи для того, чтобы начать писать, будут показаны пара примеров.
Если нужны подробности, пишите комментарии, сделаю более подробный обзор для начинающих.

Открываем видео от разработчиков: http://www.youtube.com/watch?v=gAh1bNfRLPw

По видео мы повторяем все до момента когда придет пора писать код.
Вот тут мы остановимся по подробнее.
По первых: убедитесь что вы импортировали все необходимые пространства имен как указано в видео, начала вашего кода будет выглядеть как то так:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Linq;
using Sandbox.ModAPI;
using Sandbox.Common;
using Sandbox.Common.ObjectBuilders;
using Sandbox.Common.Components;



Далее мы указываем пространство имен и имя нашего класса.

namespace Bot
{
    public class Bot : MySessionComponentBase
    {
    }
}


И тут разработчики предлагают нам на выбор несколько вариантов как можно активировать свой код.

There are several ways to this
You add handler on MyAPIGateway.Utils.MessageEntered and execute your code depending on player input to chat
You can subclass MySessionComponentBase, add Attribute and it will get loaded before world and then updated each frame.
You can subclass MyGameLogicComponent, add Attribute and it will get hooked to any object


1) Можно повесить обработчик события MyAPIGateway.Utils.MessageEntered (отправлено сообщение в чат) и по срабатыванию триггера этого события вызывать вашу функцию с кодом.
2) Можно расширить класс MySessionComponentBase, в таком случае ваш код может выполняться при каждой перерисовке, либо при загрузке единожды, что мы и сделаем в примере ниже.
3) И можно расширить класс MyGameLogicComponent который позволит привящать к любому игровому объекту. Об этом напишу если будут желающие.

И так представляю полную версию кода примитивного мода, который лишь дублирует наши сообщения в чате и выводит уведомление.

using Sandbox.ModAPI;
using Sandbox.Common;
using Sandbox.Common.ObjectBuilders;
using Sandbox.Common.Components;
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Linq;

namespace Bot
{
    [Sandbox.Common.MySessionComponentDescriptor(Sandbox.Common.MyUpdateOrder.BeforeSimulation)]
    public class Bot : MySessionComponentBase
    {
        public override void BeforeStart()
        {
            base.BeforeStart();
            MyAPIGateway.Utilities.MessageEntered += new MessageEnteredDel((string messageText, ref bool sendToOthers) => onMessage(messageText));
        }

        private void onMessage(String messageText)
        {
            MyAPIGateway.Utilities.ShowMessage("Bot", "player say: " + messageText);
            MyAPIGateway.Utilities.ShowNotification("My notification!");
        }
    }
}


Давайте теперь разберем более подробно.
public class Bot : MySessionComponentBase - Мы расширили базовый класс

public override void BeforeStart() - переопределили базовый метод

MyAPIGateway.Utilities.MessageEntered += new MessageEnteredDel((string messageText, ref bool sendToOthers) => onMessage(messageText)); - И добавили обработчик событий, который при появлении сообщения в чате вызывает метод onMessage

Внутри метода onMessage мы обращаемся к 2 методам:
1) MyAPIGateway.Utilities.ShowMessage() - Пишет сообщение в чат, в качестве первого аргумента принимается имя отправителя (ник), второй аргумент - само сообщение.

2) MyAPIGateway.Utilities.ShowNotification() - Отображает текстовое уведомление на экране игрока.

Сохраняем полученный код, как bot.cs,
Заходим по пути - (У вас буква диска может быть другой)C:\Users\[Ваш пользователь]\AppData\Roaming\Space Engineers\
Создаем папку Mods, если ее еще нет, в ней создаем папки Bot\Data\Scripts\BotScript\ сюда кладем наш bot.cs
Полный путь у меня выглядит так:
C:\Users\sudoroot\AppData\Roaming\Space Engineers\Bot\Data\Scripts\BotScript\bot.cs
Запускаем игру, либо создаем новый мир, либо загружаем уже созданный, до загрузки вам надо лишь зайти в настройки этого мира - mods - и добавить наш мод.

Вот такой небольшой и нехитрый код, позволил нам обратится к игровому API.
Если эта статья понравится, напишу более подробно и более детально.