Просмотр исходного кода

add api to fire based processing component

Kolja Strohm 4 месяцев назад
Родитель
Сommit
33990e1a5d

+ 53 - 0
FactoryCraft/BasicBlocks.cpp

@@ -21,6 +21,39 @@ void BasicBlock::addComponent(BlockComponent* component)
     components.add(component);
 }
 
+Framework::XML::Element* BasicBlock::getTargetUIML() const
+{
+    Framework::XML::Element* element
+        = Block::getTargetUIML(); // Get the base element from Block class
+    int index = 0;
+    Framework::XML::Element* last
+        = element->getChildCount() > 0
+            ? element->zChild(element->getChildCount() - 1)
+            : 0;
+    for (BlockComponent* component : components)
+    {
+        Framework::XML::Element* compElement = component->getTooltipUIML();
+        if (compElement)
+        {
+            if (last)
+            {
+                if (!last->hasAttribute("id"))
+                {
+                    last->setAttribute(
+                        "id", Framework::Text("_component_") + (index - 1));
+                }
+                compElement->setAttribute(
+                    "alignTop", last->getAttributeValue("id"));
+                compElement->setAttribute("marginTop", "5");
+            }
+            element->addChild(compElement);
+            last = compElement;
+        }
+        index++;
+    }
+    return element;
+}
+
 bool BasicBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 {
     bool ative = false;
@@ -51,6 +84,26 @@ TickSourceType BasicBlock::isTickSource() const
                                              : TickSourceType::NONE;
 }
 
+void BasicBlock::onApiCall(char messageType,
+    Framework::StreamReader* zRequest,
+    NetworkMessage* zResponse,
+    Entity* zSource)
+{
+    switch (messageType)
+    {
+    case 2: // component request
+        {
+            int index;
+            zRequest->lese((char*)&index, sizeof(int));
+            if (index >= 0 && index < components.getEintragAnzahl())
+            {
+                components.z(index)->api(zRequest, zResponse, zSource);
+            }
+            break;
+        }
+    }
+}
+
 BasicBlockType::BasicBlockType()
     : BlockType(),
       itemTypeName(),

+ 5 - 0
FactoryCraft/BasicBlocks.h

@@ -22,11 +22,16 @@ public:
         int dimensionId,
         bool hasInventory);
     void addComponent(BlockComponent* component);
+    virtual Framework::XML::Element* getTargetUIML() const override;
     virtual bool onTick(
         TickQueue* zQueue, int numTicks, bool& blocked) override;
     virtual void onPostTick() override;
     void getLightEmisionColor(unsigned char* result) const override;
     virtual TickSourceType isTickSource() const override;
+    virtual void onApiCall(char messageType,
+        Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse,
+        Entity* zSource) override;
 
     friend BasicBlockType;
 };

+ 23 - 10
FactoryCraft/Block.cpp

@@ -147,6 +147,12 @@ void Block::broadcastPassableSpeedModifierChange()
     broadcastMessage(message);
 }
 
+void Block::onApiCall(char messageType,
+    Framework::StreamReader* zRequest,
+    NetworkMessage* zResponse,
+    Entity* zSource)
+{}
+
 void Block::tick(TickQueue* zQueue)
 {
     if (wasTicked) return;
@@ -246,7 +252,7 @@ void Block::onUnloaded()
         structure->onBlockUnloaded(this);
 }
 
-Framework::Text Block::getTargetUIML()
+Framework::XML::Element* Block::getTargetUIML() const
 {
     return Game::INSTANCE->zBlockType(typeId)->getTargetUIML();
 }
@@ -266,7 +272,9 @@ bool Block::interact(Item* zItem, Entity* zActor, bool& itemChanged)
     return false;
 }
 
-void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse)
+void Block::api(Framework::StreamReader* zRequest,
+    NetworkMessage* zResponse,
+    Entity* zSource)
 {
     char id = 0;
     zRequest->lese(&id, 1);
@@ -277,14 +285,19 @@ void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse)
         sendModelInfo(zResponse);
         break;
     case 1: // dialog closed
-        short nameLen;
-        zRequest->lese((char*)&nameLen, 2);
-        char* name = new char[nameLen + 1];
-        zRequest->lese(name, nameLen);
-        name[nameLen] = 0;
-        onDialogClosed(name);
-        delete[] name;
-        break;
+        {
+            short nameLen;
+            zRequest->lese((char*)&nameLen, 2);
+            char* name = new char[nameLen + 1];
+            zRequest->lese(name, nameLen);
+            name[nameLen] = 0;
+            onDialogClosed(name);
+            delete[] name;
+            break;
+        }
+        // 2 is handled in BasicBlock
+    default: // component request handled in BasicBlock
+        onApiCall(id, zRequest, zResponse, zSource);
     }
 }
 

+ 8 - 2
FactoryCraft/Block.h

@@ -76,6 +76,10 @@ protected:
     void broadcastModelInfoChange();
     void broadcastMessage(NetworkMessage* message);
     void broadcastPassableSpeedModifierChange();
+    virtual void onApiCall(char messageType,
+        Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse,
+        Entity* zSource);
 
 public:
     Block(int typeId,
@@ -93,10 +97,12 @@ public:
         Direction dir, Framework::Either<Block*, int> neighbor);
     virtual void setNeighbourBlock(Direction dir, Block* zN);
     virtual void setNeighbourType(Direction dir, int type);
-    virtual Framework::Text getTargetUIML();
+    virtual Framework::XML::Element* getTargetUIML() const;
     virtual void sendModelInfo(NetworkMessage* zMessage);
     virtual bool interact(Item* zItem, Entity* zActor, bool& itemChanged);
-    void api(Framework::StreamReader* zRequest, NetworkMessage* zResponse);
+    void api(Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse,
+        Entity* zSource);
     virtual TickSourceType isTickSource() const;
     virtual bool needsTick() const;
     const BlockType* zBlockType() const;

+ 8 - 1
FactoryCraft/BlockComponent.h

@@ -6,6 +6,9 @@
 #include <XML.h>
 
 class Block;
+class Entity;
+
+class NetworkMessage;
 
 class BlockComponent : public virtual Framework::ReferenceCounter
 {
@@ -13,7 +16,11 @@ public:
     BlockComponent();
     virtual void initialize(Block* zBlock) = 0;
     virtual bool tick(int numTicks) = 0;
-    virtual Framework::XML::Element* getUIML() const = 0;
+    virtual void api(Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse,
+        Entity* zSource)
+        = 0;
+    virtual Framework::XML::Element* getTooltipUIML() const = 0;
     virtual void loadComponent(Framework::StreamReader* zReader) = 0;
     virtual void saveComponent(Framework::StreamWriter* zWriter) const = 0;
     virtual bool isLightSource() const;

+ 5 - 3
FactoryCraft/BlockType.cpp

@@ -163,10 +163,12 @@ void BlockType::writeTypeInfo(StreamWriter* zWriter) const
     zModel()->writeTo(zWriter);
 }
 
-Framework::Text BlockType::getTargetUIML() const
+Framework::XML::Element* BlockType::getTargetUIML() const
 {
-    return Text("<targetInfo><text width=\"auto\" height=\"auto\">") + name
-         + "</text></targetInfo>";
+    return new Framework::XML::Element(
+        Framework::Text(
+            "<targetInfo><text id=\"type\" width=\"auto\" height=\"auto\">")
+        + name + "</text></targetInfo>");
 }
 
 Block* BlockType::loadBlock(Framework::Vec3<int> position,

+ 2 - 1
FactoryCraft/BlockType.h

@@ -4,6 +4,7 @@
 #include <Either.h>
 #include <Vec3.h>
 #include <Writer.h>
+#include <XML.h>
 
 #include "DropConfig.h"
 #include "InteractionConfig.h"
@@ -64,7 +65,7 @@ public:
     virtual ItemType* createItemType() const = 0;
 
     void writeTypeInfo(Framework::StreamWriter* zWriter) const;
-    virtual Framework::Text getTargetUIML() const;
+    virtual Framework::XML::Element* getTargetUIML() const;
     virtual Block* loadBlock(Framework::Vec3<int> position,
         Framework::StreamReader* zReader,
         int dimensionId) const;

+ 2 - 2
FactoryCraft/CraftingStorage.cpp

@@ -42,14 +42,14 @@ BasicShapedCrafter::BasicShapedCrafter(
     zInventory->registerAfterPullStackCall(onChange);
     zInventory->registerAfterPushStackCall(onChange);
     zInventory->registerObserverAddedCall(
-        [this](Entity* zSource, Framework::Text id) {
+        [this](Entity* zSource, Framework::Text id, int processor) {
             Recipie* old = currentRecipie;
             calculateOutputPreview();
             if (old == currentRecipie)
             {
                 NetworkMessage* message = new NetworkMessage();
                 getOutputPreview(message);
-                message->addressUIElement(id);
+                message->addressUIElement(id, processor);
                 Game::INSTANCE->sendMessage(message, zSource);
             }
         });

+ 1 - 1
FactoryCraft/Dimension.cpp

@@ -104,7 +104,7 @@ void Dimension::api(Framework::InMemoryBuffer* zRequest,
             Framework::Either<Block*, int> block = zBlock(location, 0);
             if (block.isA())
             {
-                block.getA()->api(zRequest, zResponse);
+                block.getA()->api(zRequest, zResponse, zSource);
             }
             break;
         }

+ 14 - 60
FactoryCraft/Entity.cpp

@@ -132,7 +132,7 @@ void ActionTarget::toMessage(
         }
         else
         {
-            Framework::Text targetUIML = "";
+            Framework::XML::Element* targetUIML = 0;
             auto block
                 = Game::INSTANCE->zBlockAt(zTarget->blockPos, dimensionId, 0);
             if (block.isA())
@@ -144,16 +144,18 @@ void ActionTarget::toMessage(
                 targetUIML
                     = Game::INSTANCE->zBlockType(block.getB())->getTargetUIML();
             }
-            char* message = new char[18 + targetUIML.getLength() + 2];
+            Framework::Text targetUIMLText
+                = targetUIML ? targetUIML->toString() : Framework::Text();
+            char* message = new char[18 + targetUIMLText.getLength() + 2];
             message[0] = 3;
             message[1] = 2;
             *(int*)(message + 2) = zTarget->blockPos.x;
             *(int*)(message + 6) = zTarget->blockPos.y;
             *(int*)(message + 10) = zTarget->blockPos.z;
             *(int*)(message + 14) = zTarget->targetBlockSide;
-            short len = (short)targetUIML.getLength();
+            short len = (short)targetUIMLText.getLength();
             *(short*)(message + 18) = len;
-            memcpy(message + 20, targetUIML.getText(), len);
+            memcpy(message + 20, targetUIMLText.getText(), len);
             zMsg->setMessage(message, 18 + len + 2);
         }
     }
@@ -548,61 +550,9 @@ void Entity::calculateTarget(Framework::Vec3<float> basePos,
     }
 }
 
-void Entity::removeStatusBarObserver(Entity* zSource, Framework::Text id)
-{
-    cs.lock();
-    int index = 0;
-    for (auto observer : statusBarObservers)
-    {
-        if (observer.getFirst() == zSource->getId()
-            && observer.getSecond().istGleich(id))
-        {
-            statusBarObservers.remove(index);
-            break;
-        }
-        index++;
-    }
-    cs.unlock();
-}
-
-void Entity::addStatusBarObserver(Entity* zSource, Framework::Text id)
-{
-    cs.lock();
-    for (auto observer : statusBarObservers)
-    {
-        if (observer.getFirst() == zSource->getId()
-            && observer.getSecond().istGleich(id))
-        {
-            cs.unlock();
-            return;
-        }
-    }
-    statusBarObservers.add(
-        Framework::ImmutablePair<int, Framework::Text>(zSource->getId(), id));
-    cs.unlock();
-}
-
 void Entity::notifyStatusBarObservers(NetworkMessage* msg)
 {
-    cs.lock();
-    int index = 0;
-    Framework::Array<int> toDelete;
-    for (auto observer : statusBarObservers)
-    {
-        Entity* e = Game::INSTANCE->zEntity(observer.getFirst());
-        if (e)
-        {
-            msg->addressUIElement(observer.getSecond());
-            Game::INSTANCE->sendMessage(msg->clone(), e);
-        }
-        else
-            toDelete.add(index, 0);
-        index++;
-    }
-    for (int i : toDelete)
-        statusBarObservers.remove(i);
-    cs.unlock();
-    msg->release();
+    statusBarObservable.notifyObservers(msg);
 }
 
 ItemSkill* Entity::zSkill(int itemType)
@@ -724,8 +674,10 @@ void Entity::api(Framework::StreamReader* zRequest,
             char* guiId = new char[(int)len + 1];
             zRequest->lese(guiId, len);
             guiId[(int)len] = 0;
-            zResponse->addressUIElement(guiId);
-            addStatusBarObserver(zSource, guiId);
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            zResponse->addressUIElement(guiId, processor);
+            statusBarObservable.addObserver(zSource, guiId, processor);
             char* msg = new char[33];
             msg[0] = 0;
             *(float*)(msg + 1) = getMaxHP();
@@ -747,7 +699,9 @@ void Entity::api(Framework::StreamReader* zRequest,
             char* guiId = new char[(int)len + 1];
             zRequest->lese(guiId, len);
             guiId[(int)len] = 0;
-            removeStatusBarObserver(zSource, guiId);
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            statusBarObservable.removeObserver(zSource, guiId, processor);
             delete[] guiId;
             break;
         }

+ 2 - 4
FactoryCraft/Entity.h

@@ -11,6 +11,7 @@
 #include "ItemSkill.h"
 #include "ModelInfo.h"
 #include "NetworkMessage.h"
+#include "UIObservable.h"
 
 class EntityType;
 class Dimension;
@@ -52,6 +53,7 @@ struct MovementFrame
 class Entity : public Inventory
 {
 private:
+    UIObservable statusBarObservable;
     float stamina;
     float hunger;
     float currentHP;
@@ -81,8 +83,6 @@ protected:
     Framework::ZeitMesser time;
     Framework::Array<MovementFrame> movements;
     Framework::Critical cs;
-    Framework::Array<Framework::ImmutablePair<int, Framework::Text>>
-        statusBarObservers;
 
     virtual void onDeath(
         Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill);
@@ -95,8 +95,6 @@ protected:
     void calculateTarget(Framework::Vec3<float> basePos,
         Framework::Vec3<float> direction,
         const Item* zItem);
-    void removeStatusBarObserver(Entity* zSource, Framework::Text id);
-    void addStatusBarObserver(Entity* zSource, Framework::Text id);
     void notifyStatusBarObservers(NetworkMessage* msg);
     ItemSkill* zSkill(int itemType);
 

+ 2 - 0
FactoryCraft/FactoryCraft.vcxproj

@@ -217,6 +217,7 @@
     <ClInclude Include="UIElement.h" />
     <ClInclude Include="UIFuelState.h" />
     <ClInclude Include="UIInventory.h" />
+    <ClInclude Include="UIObservable.h" />
     <ClInclude Include="UIReference.h" />
     <ClInclude Include="UIText.h" />
     <ClInclude Include="WorldGenerator.h" />
@@ -341,6 +342,7 @@
     <ClCompile Include="UIElement.cpp" />
     <ClCompile Include="UIFuelState.cpp" />
     <ClCompile Include="UIInventory.cpp" />
+    <ClCompile Include="UIObservable.cpp" />
     <ClCompile Include="UIReference.cpp" />
     <ClCompile Include="UIText.cpp" />
     <ClCompile Include="WorldGenerator.cpp" />

+ 9 - 0
FactoryCraft/FactoryCraft.vcxproj.filters

@@ -124,6 +124,9 @@
     <Filter Include="world\blocks\components">
       <UniqueIdentifier>{fb4a3a5c-a42f-4674-97eb-825dfcc5d1b6}</UniqueIdentifier>
     </Filter>
+    <Filter Include="UI\Observable">
+      <UniqueIdentifier>{545fce4e-6aa4-4b61-8e95-ece0430bb029}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Chunk.h">
@@ -504,6 +507,9 @@
     <ClInclude Include="UIFuelState.h">
       <Filter>UI\UIElements</Filter>
     </ClInclude>
+    <ClInclude Include="UIObservable.h">
+      <Filter>UI\Observable</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">
@@ -872,5 +878,8 @@
     <ClCompile Include="UIFuelState.cpp">
       <Filter>UI\UIElements</Filter>
     </ClCompile>
+    <ClCompile Include="UIObservable.cpp">
+      <Filter>UI\Observable</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 154 - 8
FactoryCraft/FireBasedProcessingBlockComponent.cpp

@@ -7,13 +7,20 @@
 #include "RecipieLoader.h"
 
 FireBasedProcessingBlockComponent::FireBasedProcessingBlockComponent()
-    : BlockComponent()
+    : BlockComponent(),
+      burnLight(0),
+      ticksNeeded(0),
+      currentRecipie(0),
+      maxFuelBuffer(0),
+      fuelBuffer(0),
+      burning(0)
 {}
 
 FireBasedProcessingBlockComponent::~FireBasedProcessingBlockComponent() {}
 
-void FireBasedProcessingBlockComponent::findRecipie()
+bool FireBasedProcessingBlockComponent::findRecipie()
 {
+    bool changed = 0;
     RecipieList* zRecipies
         = Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup.getText());
     if (zRecipies)
@@ -25,13 +32,16 @@ void FireBasedProcessingBlockComponent::findRecipie()
             recipie->consumeInputs(this);
             currentRecipie = recipie;
             ticksNeeded = recipie->getTicksNeeded();
+            changed = 1;
         }
         zBlock->unlock();
     }
+    return changed;
 }
 
-void FireBasedProcessingBlockComponent::consumeFuel()
+bool FireBasedProcessingBlockComponent::consumeFuel()
 {
+    bool changed = 0;
     zBlock->lock();
     ItemSlot* fireStartingSlot = 0;
     ItemSlot* fuelSlot = 0;
@@ -66,7 +76,9 @@ void FireBasedProcessingBlockComponent::consumeFuel()
             {
                 // TODO: check if item is burnable and how much fuel it provides
                 fuelBuffer += 100;
+                maxFuelBuffer = fuelBuffer;
                 fuelStack->release();
+                changed = 1;
             }
             else
             {
@@ -96,13 +108,35 @@ void FireBasedProcessingBlockComponent::consumeFuel()
             {
                 // TODO: check if item is burnable and how much fuel it provides
                 fuelBuffer += 100;
+                maxFuelBuffer = fuelBuffer;
                 burning = true;
                 fuelStack->release();
                 fireStartingStack->release();
+                changed = 1;
             }
         }
     }
     zBlock->unlock();
+    return changed;
+}
+
+void FireBasedProcessingBlockComponent::createProgressMessage(
+    NetworkMessage* message) const
+{
+    char* msg = new char[8];
+    *(int*)msg = currentRecipie ? currentRecipie->getTicksNeeded() : 0;
+    *(int*)(msg + 4)
+        = currentRecipie ? currentRecipie->getTicksNeeded() - ticksNeeded : 0;
+    message->setMessage(msg, 8);
+}
+
+void FireBasedProcessingBlockComponent::createFuelMessage(
+    NetworkMessage* message) const
+{
+    char* msg = new char[8];
+    *(int*)msg = maxFuelBuffer;
+    *(int*)(msg + 4) = fuelBuffer;
+    message->setMessage(msg, 8);
 }
 
 void FireBasedProcessingBlockComponent::initialize(Block* zBlock)
@@ -113,11 +147,13 @@ void FireBasedProcessingBlockComponent::initialize(Block* zBlock)
 bool FireBasedProcessingBlockComponent::tick(int numTicks)
 {
     bool active = 0;
+    bool progressChanged = 0;
+    bool fuelChanged = 0;
     while (numTicks > 0)
     {
         if (!fuelBuffer)
         {
-            consumeFuel();
+            fuelChanged |= consumeFuel();
         }
         if (!burning)
         {
@@ -125,7 +161,7 @@ bool FireBasedProcessingBlockComponent::tick(int numTicks)
         }
         if (!currentRecipie)
         {
-            findRecipie();
+            progressChanged |= findRecipie();
         }
         bool processed = false;
         if (currentRecipie)
@@ -134,7 +170,7 @@ bool FireBasedProcessingBlockComponent::tick(int numTicks)
                 = fuelBuffer / currentRecipie->getFuelPerTickNeeded();
             if (!possibleTicks)
             {
-                consumeFuel();
+                fuelChanged |= consumeFuel();
                 possibleTicks
                     = fuelBuffer / currentRecipie->getFuelPerTickNeeded();
             }
@@ -158,6 +194,8 @@ bool FireBasedProcessingBlockComponent::tick(int numTicks)
                         -= currentRecipie->getFuelPerTickNeeded() * numTicks;
                     numTicks = 0;
                 }
+                progressChanged = 1;
+                fuelChanged = 1;
                 processed = true;
             }
             else
@@ -173,6 +211,8 @@ bool FireBasedProcessingBlockComponent::tick(int numTicks)
                     ticksNeeded = 0;
                     currentRecipie = 0;
                     processed = true;
+                    fuelChanged = 1;
+                    progressChanged = 1;
                 }
                 else
                 {
@@ -181,6 +221,8 @@ bool FireBasedProcessingBlockComponent::tick(int numTicks)
                                 * possibleTicks;
                     ticksNeeded -= possibleTicks;
                     processed = possibleTicks > 0;
+                    progressChanged = possibleTicks > 0;
+                    fuelChanged = possibleTicks > 0;
                 }
             }
         }
@@ -191,21 +233,123 @@ bool FireBasedProcessingBlockComponent::tick(int numTicks)
             {
                 fuelBuffer -= numTicks;
                 numTicks = 0;
+                fuelChanged = 1;
             }
             else
             {
+                fuelChanged = fuelBuffer > 0;
                 numTicks -= fuelBuffer;
                 fuelBuffer = 0;
             }
         }
         active = 1;
     }
+    if (fuelChanged)
+    {
+        NetworkMessage* fuelMessage = new NetworkMessage();
+        createFuelMessage(fuelMessage);
+        fuelObservable.notifyObservers(fuelMessage);
+    }
+    if (progressChanged)
+    {
+        NetworkMessage* progressMessage = new NetworkMessage();
+        createProgressMessage(progressMessage);
+        progressObservable.notifyObservers(progressMessage);
+    }
     return active;
 }
 
-Framework::XML::Element* FireBasedProcessingBlockComponent::getUIML() const
+void FireBasedProcessingBlockComponent::api(Framework::StreamReader* zRequest,
+    NetworkMessage* zResponse,
+    Entity* zSource)
+{
+    char type;
+    zRequest->lese(&type, 1);
+    switch (type)
+    {
+    case 0: // subscribe to fuel
+        {
+            char idLen;
+            zRequest->lese(&idLen, 1);
+            char* id = new char[idLen + 1];
+            zRequest->lese(id, idLen);
+            id[(int)idLen] = 0;
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            fuelObservable.addObserver(zSource, id, processor);
+            createFuelMessage(zResponse);
+            zResponse->addressUIElement(id, processor);
+            break;
+        }
+    case 1: // subscribe to progress
+        {
+            char idLen;
+            zRequest->lese(&idLen, 1);
+            char* id = new char[idLen + 1];
+            zRequest->lese(id, idLen);
+            id[(int)idLen] = 0;
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            progressObservable.addObserver(zSource, id, processor);
+            createProgressMessage(zResponse);
+            zResponse->addressUIElement(id, processor);
+            break;
+        }
+    case 2: // unsubscribe to fuel
+        {
+            char idLen;
+            zRequest->lese(&idLen, 1);
+            char* id = new char[idLen + 1];
+            zRequest->lese(id, idLen);
+            id[(int)idLen] = 0;
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            fuelObservable.removeObserver(zSource, id, processor);
+            break;
+        }
+    case 3: // unsubscribe to progress
+        {
+            char idLen;
+            zRequest->lese(&idLen, 1);
+            char* id = new char[idLen + 1];
+            zRequest->lese(id, idLen);
+            id[(int)idLen] = 0;
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            progressObservable.removeObserver(zSource, id, processor);
+            break;
+        }
+    }
+}
+
+Framework::XML::Element*
+FireBasedProcessingBlockComponent::getTooltipUIML() const
 {
-    return nullptr;
+    if (currentRecipie)
+    {
+        Framework::XML::Element* result = new Framework::XML::Element(
+            "<text width=\"auto\" height=\"auto\"></text>");
+        Framework::Text content;
+        content.append() << "Processing: ";
+        int first = 1;
+        for (const ItemInfo& output : currentRecipie->getOutput())
+        {
+            if (!first)
+            {
+                content.append() << ", ";
+            }
+            content.append()
+                << output.count << " "
+                << Game::INSTANCE->zItemType(output.type)->getTooltipUIML();
+            first = 0;
+        }
+        content.append()
+            << " ("
+            << (int)((ticksNeeded / currentRecipie->getTicksNeeded()) * 100)
+            << "%)";
+        result->setText(content);
+    }
+    return 0;
 }
 
 bool FireBasedProcessingBlockComponent::isAllAvailable(
@@ -249,6 +393,7 @@ void FireBasedProcessingBlockComponent::loadComponent(
     Framework::StreamReader* zReader)
 {
     zReader->lese((char*)&ticksNeeded, 4);
+    zReader->lese((char*)&maxFuelBuffer, 4);
     zReader->lese((char*)&fuelBuffer, 4);
     zReader->lese((char*)&burning, 1);
     int index = 0;
@@ -270,6 +415,7 @@ void FireBasedProcessingBlockComponent::saveComponent(
     Framework::StreamWriter* zWriter) const
 {
     zWriter->schreibe((char*)&ticksNeeded, 4);
+    zWriter->schreibe((char*)&maxFuelBuffer, 4);
     zWriter->schreibe((char*)&fuelBuffer, 4);
     zWriter->schreibe((char*)&burning, 1);
     int index = -1;

+ 12 - 3
FactoryCraft/FireBasedProcessingBlockComponent.h

@@ -3,6 +3,7 @@
 #include "BlockComponent.h"
 #include "CraftingStorage.h"
 #include "TypeRegistry.h"
+#include "UIObservable.h"
 
 class ItemFilter;
 
@@ -10,6 +11,8 @@ class FireBasedProcessingBlockComponent : public BlockComponent,
                                           public CraftingStorage
 {
 private:
+    UIObservable fuelObservable;
+    UIObservable progressObservable;
     Block* zBlock;
     ItemFilter* fireStartingItemFilter;
     ItemFilter* fuelItemFilter;
@@ -21,6 +24,7 @@ private:
     int burnLight;
     int ticksNeeded;
     Recipie* currentRecipie;
+    int maxFuelBuffer;
     int fuelBuffer;
     bool burning;
 
@@ -29,13 +33,18 @@ public:
     virtual ~FireBasedProcessingBlockComponent();
 
 protected:
-    void findRecipie();
-    void consumeFuel();
+    bool findRecipie();
+    bool consumeFuel();
+    void createProgressMessage(NetworkMessage* message) const;
+    void createFuelMessage(NetworkMessage* message) const;
 
 public:
     virtual void initialize(Block* zBlock) override;
     virtual bool tick(int numTicks) override;
-    virtual Framework::XML::Element* getUIML() const override;
+    virtual void api(Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse,
+        Entity* zSource) override;
+    virtual Framework::XML::Element* getTooltipUIML() const override;
     virtual bool isAllAvailable(
         Framework::RCArray<RecipieInput>& inputs) override;
     virtual bool hasFreeSpace(const Item* zItem, int amount) override;

+ 14 - 60
FactoryCraft/Inventory.cpp

@@ -435,60 +435,7 @@ void Inventory::saveInventory(Framework::StreamWriter* zWriter)
 
 void Inventory::notifyObservers(NetworkMessage* msg)
 {
-    cs.lock();
-    int index = 0;
-    Array<int> toDelete;
-    for (auto observer : observers)
-    {
-        Entity* e = Game::INSTANCE->zEntity(observer.getFirst());
-        if (e)
-        {
-            msg->addressUIElement(observer.getSecond());
-            Game::INSTANCE->sendMessage(msg->clone(), e);
-        }
-        else
-            toDelete.add(index, 0);
-        index++;
-    }
-    for (int i : toDelete)
-        observers.remove(i);
-    cs.unlock();
-    msg->release();
-}
-
-void Inventory::removeObserver(Entity* zSource, Framework::Text id)
-{
-    cs.lock();
-    int index = 0;
-    for (auto observer : observers)
-    {
-        if (observer.getFirst() == zSource->getId()
-            && observer.getSecond().istGleich(id))
-        {
-            observers.remove(index);
-            break;
-        }
-        index++;
-    }
-    cs.unlock();
-}
-
-void Inventory::addObserver(Entity* zSource, Framework::Text id)
-{
-    cs.lock();
-    for (auto observer : observers)
-    {
-        if (observer.getFirst() == zSource->getId()
-            && observer.getSecond().istGleich(id))
-        {
-            cs.unlock();
-            return;
-        }
-    }
-    observers.add(ImmutablePair<int, Text>(zSource->getId(), id));
-    cs.unlock();
-    for (auto call : observerAddedCalls)
-        call(zSource, id);
+    observable.notifyObservers(msg);
 }
 
 void Inventory::lock()
@@ -811,8 +758,10 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest,
             char* id = new char[idLen + 1];
             zRequest->lese(id, idLen);
             id[(int)idLen] = 0;
-            zResponse->addressUIElement(id);
-            addObserver(zSource, id);
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            zResponse->addressUIElement(id, processor);
+            observable.addObserver(zSource, id, processor);
             delete[] id;
             char filterLen;
             zRequest->lese(&filterLen, 1);
@@ -868,7 +817,9 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest,
             char* id = new char[idLen + 1];
             zRequest->lese(id, idLen);
             id[(int)idLen] = 0;
-            removeObserver(zSource, id);
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            observable.removeObserver(zSource, id, processor);
             delete[] id;
             break;
         }
@@ -879,7 +830,9 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest,
             char* id = new char[idLen + 1];
             zRequest->lese(id, idLen);
             id[(int)idLen] = 0;
-            zResponse->addressUIElement(id);
+            int processor;
+            zRequest->lese((char*)&processor, 4);
+            zResponse->addressUIElement(id, processor);
             delete[] id;
             int slotId;
             zRequest->lese((char*)&slotId, 4);
@@ -917,9 +870,10 @@ void Inventory::registerAfterPushStackCall(std::function<void(
 }
 
 void Inventory::registerObserverAddedCall(
-    std::function<void(Entity* zSource, Framework::Text id)> call)
+    std::function<void(Entity* zSource, Framework::Text id, int processor)>
+        call)
 {
-    observerAddedCalls.add(call);
+    observable.registerOnObserverAddedCall(call);
 }
 
 int Inventory::getDimensionId() const

+ 11 - 6
FactoryCraft/Inventory.h

@@ -10,6 +10,7 @@
 #include "Area.h"
 #include "ItemSlot.h"
 #include "Recipie.h"
+#include "UIObservable.h"
 
 class ItemFilter;
 class Inventory;
@@ -58,10 +59,17 @@ public:
     void lock();
 };
 
+struct InventoryObserver
+{
+    int entityId;
+    Framework::Text uimlId;
+    int processorId;
+};
+
 class Inventory : public virtual Framework::ReferenceCounter
 {
 private:
-    Framework::Array<Framework::ImmutablePair<int, Framework::Text>> observers;
+    UIObservable observable;
     Framework::RCArray<ItemSlot>* pullSlotsOrder;
     Framework::RCArray<ItemSlot>* pushSlotsOrder;
     Framework::HashMap<int, Framework::Array<ItemSlot*>*>* itemCache;
@@ -72,8 +80,6 @@ private:
     Framework::Array<std::function<void(
         ItemSlot* zSlot, Direction dir, const Item* zItem, int count)>>
         afterPushStackCalls;
-    Framework::Array<std::function<void(Entity* zSource, Framework::Text id)>>
-        observerAddedCalls;
     int nextSlotId;
 
 public:
@@ -99,8 +105,6 @@ protected:
     virtual void updateSlot(ItemSlot* zSlot);
     virtual void loadInventory(Framework::StreamReader* zReader);
     virtual void saveInventory(Framework::StreamWriter* zWriter);
-    void removeObserver(Entity* zSource, Framework::Text id);
-    void addObserver(Entity* zSource, Framework::Text id);
     virtual void addItems(
         ItemStack* zItems, Direction dir, ItemFilter* zFilter);
 
@@ -140,7 +144,8 @@ public:
             ItemSlot* zSlot, Direction dir, const Item* zItem, int count)>
             call);
     void registerObserverAddedCall(
-        std::function<void(Entity* zSource, Framework::Text id)> call);
+        std::function<void(Entity* zSource, Framework::Text id, int processor)>
+            call);
     int getDimensionId() const;
     Framework::Vec3<float> getLocation() const;
 

+ 4 - 2
FactoryCraft/NetworkMessage.cpp

@@ -117,15 +117,17 @@ void NetworkMessage::updateDialog(
     setMessage(msg, msgSize);
 }
 
-void NetworkMessage::addressUIElement(Framework::Text elementId)
+void NetworkMessage::addressUIElement(
+    Framework::Text elementId, int processorId)
 {
     delete[] address;
-    addressLength = (char)(4 + elementId.getLength());
+    addressLength = (char)(8 + elementId.getLength());
     address = new char[addressLength];
     address[0] = 2;                                        // gui message
     address[1] = 1;                                        // element message
     *(short*)(address + 2) = (short)elementId.getLength(); // element id
     memcpy(address + 4, elementId.getText(), elementId.getLength());
+    *(int*)(address + 4 + elementId.getLength()) = processorId; // processor id
 }
 
 void NetworkMessage::setMessage(char* msg, int length)

+ 1 - 1
FactoryCraft/NetworkMessage.h

@@ -36,7 +36,7 @@ public:
     void addressDimension(const Dimension* zDim);
     void openDialog(Framework::Text dialogName, Framework::Text uiml);
     void updateDialog(Framework::Text dialogName, Framework::Text uiml);
-    void addressUIElement(Framework::Text elementId);
+    void addressUIElement(Framework::Text elementId, int processorId);
     void setMessage(char* msg, int length);
     void sendChatMessage(ChatMessage* zMsg);
     void sendChatOptions(ChatObserver* zOptions);

+ 2 - 2
FactoryCraft/Player.cpp

@@ -227,7 +227,7 @@ void Player::playerApi(
             zRequest->lese((char*)&leftHandPosition, 4);
             leftHandPosition = leftHandPosition % itemBar.getEintragAnzahl();
             NetworkMessage* msg = new NetworkMessage();
-            msg->addressUIElement("gui_item_bar");
+            msg->addressUIElement("gui_item_bar", -1);
             char* message = new char[5];
             message[0] = 3; // set selected slot
             *(int*)(message + 1) = leftHandPosition;
@@ -337,7 +337,7 @@ void Player::playerApi(
     case 8: // request left hand position
         {
             NetworkMessage* msg = new NetworkMessage();
-            msg->addressUIElement("gui_item_bar");
+            msg->addressUIElement("gui_item_bar", -1);
             char* message = new char[5];
             message[0] = 3; // set selected slot
             *(int*)(message + 1) = leftHandPosition;

+ 80 - 0
FactoryCraft/UIObservable.cpp

@@ -0,0 +1,80 @@
+#include "UIObservable.h"
+
+#include "Entity.h"
+#include "Game.h"
+#include "NetworkMessage.h"
+
+void UIObservable::notifyObservers(NetworkMessage* msg)
+{
+    cs.lock();
+    int index = 0;
+    Framework::Array<int> toDelete;
+    for (auto observer : observers)
+    {
+        Entity* e = Game::INSTANCE->zEntity(observer.sourceEntiryId);
+        if (e)
+        {
+            msg->addressUIElement(
+                observer.uimlElementId, observer.networkApiProcessorId);
+            Game::INSTANCE->sendMessage(msg->clone(), e);
+        }
+        else
+            toDelete.add(index, 0);
+        index++;
+    }
+    for (int i : toDelete)
+        observers.remove(i);
+    cs.unlock();
+    msg->release();
+}
+
+void UIObservable::removeObserver(
+    Entity* zSource, Framework::Text uimlElementId, int networkApiProcessorId)
+{
+    cs.lock();
+    int index = 0;
+    for (auto observer : observers)
+    {
+        if (observer.sourceEntiryId == zSource->getId()
+            && observer.uimlElementId.istGleich(uimlElementId)
+            && observer.networkApiProcessorId == networkApiProcessorId)
+        {
+            observers.remove(index);
+            break;
+        }
+        index++;
+    }
+    cs.unlock();
+}
+
+void UIObservable::addObserver(
+    Entity* zSource, Framework::Text uimlElementId, int networkApiProcessorId)
+{
+    cs.lock();
+    for (auto observer : observers)
+    {
+        if (observer.sourceEntiryId == zSource->getId()
+            && observer.uimlElementId.istGleich(uimlElementId)
+            && observer.networkApiProcessorId == networkApiProcessorId)
+        {
+            cs.unlock();
+            return;
+        }
+    }
+    observers.add({zSource->getId(), uimlElementId, networkApiProcessorId});
+    for (auto call : onObserverAdded)
+    {
+        cs.unlock();
+        call(zSource, uimlElementId, networkApiProcessorId);
+        cs.lock();
+    }
+    cs.unlock();
+}
+
+void UIObservable::registerOnObserverAddedCall(std::function<void(
+        Entity* zSource, Framework::Text uimlElementId, int processor)> call)
+{
+    cs.lock();
+    onObserverAdded.add(call);
+    cs.unlock();
+}

+ 38 - 0
FactoryCraft/UIObservable.h

@@ -0,0 +1,38 @@
+#pragma once
+
+#include <Array.h>
+#include <Critical.h>
+#include <Text.h>
+
+struct UIObserver
+{
+    int sourceEntiryId;
+    Framework::Text uimlElementId;
+    int networkApiProcessorId;
+};
+
+class Entity;
+class NetworkMessage;
+
+class UIObservable
+{
+private:
+    Framework::Array<UIObserver> observers;
+    Framework::Array<std::function<void(Entity* zSource,
+        Framework::Text uimlElementId,
+        int networkApiProcessorId)>>
+        onObserverAdded;
+    Framework::Critical cs;
+
+public:
+    void notifyObservers(NetworkMessage* msg);
+    void removeObserver(Entity* zSource,
+        Framework::Text uimlElementId,
+        int networkApiProcessorId);
+    void addObserver(Entity* zSource,
+        Framework::Text uimlElementId,
+        int networkApiProcessorId);
+    void registerOnObserverAddedCall(std::function<void(Entity* zSource,
+            Framework::Text uimlElementId,
+            int networkApiProcessorId)> call);
+};

+ 2 - 0
Windows Version/Windows Version.vcxproj

@@ -276,6 +276,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\UIElement.cpp" />
     <ClCompile Include="..\FactoryCraft\UIFuelState.cpp" />
     <ClCompile Include="..\FactoryCraft\UIInventory.cpp" />
+    <ClCompile Include="..\FactoryCraft\UIObservable.cpp" />
     <ClCompile Include="..\FactoryCraft\UIReference.cpp" />
     <ClCompile Include="..\FactoryCraft\UIText.cpp" />
     <ClCompile Include="..\FactoryCraft\WorldGenerator.cpp" />
@@ -405,6 +406,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\UIElement.h" />
     <ClInclude Include="..\FactoryCraft\UIFuelState.h" />
     <ClInclude Include="..\FactoryCraft\UIInventory.h" />
+    <ClInclude Include="..\FactoryCraft\UIObservable.h" />
     <ClInclude Include="..\FactoryCraft\UIReference.h" />
     <ClInclude Include="..\FactoryCraft\UIText.h" />
     <ClInclude Include="..\FactoryCraft\WorldGenerator.h" />

+ 9 - 0
Windows Version/Windows Version.vcxproj.filters

@@ -124,6 +124,9 @@
     <Filter Include="world\blocks\components">
       <UniqueIdentifier>{a9dadb06-9971-42a7-82e0-81ac95faa38f}</UniqueIdentifier>
     </Filter>
+    <Filter Include="UI\Observable">
+      <UniqueIdentifier>{0959e746-5b3e-4592-b633-8899ba10be59}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\FactoryCraft\Server.cpp">
@@ -489,6 +492,9 @@
     <ClCompile Include="..\FactoryCraft\UIFuelState.cpp">
       <Filter>UI\UIElements</Filter>
     </ClCompile>
+    <ClCompile Include="..\FactoryCraft\UIObservable.cpp">
+      <Filter>UI\Observable</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\FactoryCraft\Chunk.h">
@@ -872,5 +878,8 @@
     <ClInclude Include="..\FactoryCraft\UIFuelState.h">
       <Filter>UI\UIElements</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\UIObservable.h">
+      <Filter>UI\Observable</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>