namespace Msp::Game {
+enum DispatchType
+{
+ DIRECT,
+ QUEUED
+};
+
template<typename P>
concept HasEntityIdField = requires(P p) { { (p.entity_id) } -> std::same_as<std::uint32_t &>; };
template<typename P, typename E>
concept ValidRemoteCall = std::is_same_v<E, void> || (std::is_base_of_v<Entity, E> && HasEntityIdField<P>);
+class MSPGAME_API not_dispatched: public std::logic_error
+{
+public:
+ not_dispatched(const std::type_info &);
+};
+
+
+class MSPGAME_API RemoteCallBase
+{
+protected:
+ Replicator *replicator = nullptr;
+
+ RemoteCallBase(Replicator *replicator);
+
+public:
+ virtual void verify_dispatched() const = 0;
+};
+
+
+template<typename P, typename E, DispatchType D>
+struct RemoteCallQueue;
+
+template<typename P, typename E>
+struct RemoteCallQueue<P, E, DIRECT> { };
template<typename P, typename E>
+struct RemoteCallQueue<P, E, QUEUED>
+{
+ struct QueuedPacket
+ {
+ Handle<E> entity;
+ P packet;
+ };
+
+ std::vector<QueuedPacket> queue;
+};
+
+template<typename P>
+struct RemoteCallQueue<P, void, QUEUED>
+{
+ std::vector<P> queue;
+};
+
+
+template<typename P, typename E, DispatchType D>
requires ValidRemoteCall<P, E>
-class RemoteCall: protected Net::PacketReceiver<P>
+class RemoteCall: protected RemoteCallBase, private RemoteCallQueue<P, E, D>, protected Net::PacketReceiver<P>
{
protected:
using Function = std::conditional_t<std::is_same_v<E, void>,
std::function<void(const P &)>, std::function<void(Handle<E>, const P &)>>;
- Replicator *replicator = nullptr;
+private:
Function func;
+protected:
RemoteCall(Replicator *, Function);
+ template<typename... Args>
+ void invoke(Args &&... a);
+
void prepare(Handle<E>, P &) const;
void receive(P &&) override;
+ void verify_dispatched() const override;
+public:
+ void dispatch();
};
-template<typename P, typename E = void>
-class ReplicatedCall: public RemoteCall<P, E>
+template<typename P, typename E = void, DispatchType D = QUEUED>
+class ReplicatedCall: public RemoteCall<P, E, D>
{
public:
- ReplicatedCall(Replicator *r, RemoteCall<P, E>::Function f): RemoteCall<P, E>(r, std::move(f)) { }
+ ReplicatedCall(Replicator *r, RemoteCall<P, E, D>::Function f): RemoteCall<P, E, D>(r, std::move(f)) { }
void operator()(Handle<E>, P &);
void operator()(unsigned, Handle<E>, P &);
};
-template<typename P>
-class ReplicatedCall<P, void>: public RemoteCall<P, void>
+template<typename P, DispatchType D>
+class ReplicatedCall<P, void, D>: public RemoteCall<P, void, D>
{
public:
- ReplicatedCall(Replicator *r, RemoteCall<P, void>::Function f): RemoteCall<P, void>(r, std::move(f)) { }
+ ReplicatedCall(Replicator *r, RemoteCall<P, void, D>::Function f): RemoteCall<P, void, D>(r, std::move(f)) { }
void operator()(const P &);
void operator()(unsigned, const P &);
};
-template<typename P, typename E = void>
-class RequestCall: public RemoteCall<P, E>
+template<typename P, typename E = void, DispatchType D = QUEUED>
+class RequestCall: public RemoteCall<P, E, D>
{
public:
- RequestCall(Replicator *r, RemoteCall<P, E>::Function f): RemoteCall<P, E>(r, std::move(f)) { }
+ RequestCall(Replicator *r, RemoteCall<P, E, D>::Function f): RemoteCall<P, E, D>(r, std::move(f)) { }
void operator()(Handle<E>, P &);
};
-template<typename P>
-class RequestCall<P, void>: public RemoteCall<P, void>
+template<typename P, DispatchType D>
+class RequestCall<P, void, D>: public RemoteCall<P, void, D>
{
public:
- RequestCall(Replicator *r, RemoteCall<P, void>::Function f): RemoteCall<P, void>(r, std::move(f)) { }
+ RequestCall(Replicator *r, RemoteCall<P, void, D>::Function f): RemoteCall<P, void, D>(r, std::move(f)) { }
void operator()(P &);
};
-template<typename P, typename E>
+template<typename P, typename E, DispatchType D>
requires ValidRemoteCall<P, E>
-RemoteCall<P, E>::RemoteCall(Replicator *r, Function f):
- replicator(r),
+RemoteCall<P, E, D>::RemoteCall(Replicator *r, Function f):
+ RemoteCallBase(r),
func(std::move(f))
{
if(replicator)
replicator->add_receiver(*this);
}
-template<typename P, typename E>
+template<typename P, typename E, DispatchType D>
requires ValidRemoteCall<P, E>
-void RemoteCall<P, E>::prepare(Handle<E> entity, P &packet) const
+template<typename... Args>
+void RemoteCall<P, E, D>::invoke(Args &&... args)
+{
+ if constexpr(D==QUEUED)
+ this->queue.emplace_back(std::forward<Args>(args)...);
+ else
+ func(std::forward<Args>(args)...);
+}
+
+template<typename P, typename E, DispatchType D>
+ requires ValidRemoteCall<P, E>
+void RemoteCall<P, E, D>::prepare(Handle<E> entity, P &packet) const
{
Handle<Zygote> zygote = entity->template get_component<Zygote>();
if(!zygote)
packet.entity_id = entity_id;
}
-template<typename P, typename E>
+template<typename P, typename E, DispatchType D>
requires ValidRemoteCall<P, E>
-void RemoteCall<P, E>::receive(P &&packet)
+void RemoteCall<P, E, D>::receive(P &&packet)
{
if constexpr(std::is_same_v<E, void>)
- this->func(packet);
+ invoke(packet);
else
{
Handle<Entity> entity = this->replicator->find_entity(packet.entity_id, this->replicator->is_server());
if(!cast_entity)
return;
- this->func(cast_entity, packet);
+ invoke(cast_entity, packet);
}
}
+template<typename P, typename E, DispatchType D>
+ requires ValidRemoteCall<P, E>
+void RemoteCall<P, E, D>::verify_dispatched() const
+{
+ if constexpr(D==QUEUED)
+ if(!this->queue.empty())
+ throw not_dispatched(typeid(P));
+}
-template<typename P, typename E>
-void ReplicatedCall<P, E>::operator()(Handle<E> entity, P &packet)
+template<typename P, typename E, DispatchType D>
+ requires ValidRemoteCall<P, E>
+void RemoteCall<P, E, D>::dispatch()
+{
+ if constexpr(D==QUEUED)
+ {
+ for(const auto &p: this->queue)
+ {
+ if constexpr(std::is_same_v<E, void>)
+ func(p);
+ else
+ func(p.entity, p.packet);
+ }
+ this->queue.clear();
+ }
+}
+
+
+template<typename P, typename E, DispatchType D>
+void ReplicatedCall<P, E, D>::operator()(Handle<E> entity, P &packet)
{
if(this->replicator)
{
this->prepare(entity, packet);
this->replicator->send(entity, packet);
}
- this->func(entity, packet);
+ this->invoke(entity, packet);
}
-template<typename P, typename E>
-void ReplicatedCall<P, E>::operator()(unsigned target, Handle<E> entity, P &packet)
+template<typename P, typename E, DispatchType D>
+void ReplicatedCall<P, E, D>::operator()(unsigned target, Handle<E> entity, P &packet)
{
if(this->replicator)
{
}
-template<typename P>
-void ReplicatedCall<P, void>::operator()(const P &packet)
+template<typename P, DispatchType D>
+void ReplicatedCall<P, void, D>::operator()(const P &packet)
{
if(this->replicator)
{
throw invalid_state("ReplicatedCall<P>::operator()");
this->replicator->send(packet);
}
- this->func(packet);
+ this->invoke(packet);
}
-template<typename P>
-void ReplicatedCall<P, void>::operator()(unsigned target, const P &packet)
+template<typename P, DispatchType D>
+void ReplicatedCall<P, void, D>::operator()(unsigned target, const P &packet)
{
if(this->replicator)
this->replicator->send(target, packet);
}
-template<typename P, typename E>
-void RequestCall<P, E>::operator()(Handle<E> entity, P &packet)
+template<typename P, typename E, DispatchType D>
+void RequestCall<P, E, D>::operator()(Handle<E> entity, P &packet)
{
if(this->replicator && !this->replicator->is_server())
{
this->replicator->send(entity, packet);
}
else
- this->func(entity, packet);
+ this->invoke(entity, packet);
}
-template<typename P>
-void RequestCall<P, void>::operator()(P &packet)
+template<typename P, DispatchType D>
+void RequestCall<P, void, D>::operator()(P &packet)
{
if(this->replicator && !this->replicator->is_server())
this->replicator->send(packet);
else
- this->func(packet);
+ this->invoke(packet);
}
} // namespace Msp::Game