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

完成 UniPacket 部分,修复部分 bug 及遗留问题

akemimadoka 7 лет назад
Родитель
Сommit
618487a102
6 измененных файлов с 271 добавлено и 16 удалено
  1. 1 1
      Extern/NatsuLib
  2. 47 3
      YumeBot.Test/JceTest.cpp
  3. 35 1
      YumeBot/Jce.cpp
  4. 82 11
      YumeBot/Jce.h
  5. 63 0
      YumeBot/Wup.cpp
  6. 43 0
      YumeBot/Wup.h

+ 1 - 1
Extern/NatsuLib

@@ -1 +1 @@
-Subproject commit e7d7021b5cae3bec13a725fa1d51efdac4ba4e46
+Subproject commit c97c2cfa0f7ecb71cc00e2af5947bb9308577f72

+ 47 - 3
YumeBot.Test/JceTest.cpp

@@ -42,13 +42,17 @@ TEST_CASE("Jce", "[Jce]")
 		CHECK(ptr->GetTestList() == std::vector{ 1.0, 2.0, 3.0 });
 	}
 
-	SECTION("Wup.UniPacket")
+	SECTION("Wup.UniAttribute")
 	{
 		using namespace Wup;
 
 		OldUniAttribute uniAttribute{};
+
 		uniAttribute.Put(u8"SomeInt"_ns, 1);
+		CHECK(uniAttribute.Get<std::int32_t>(u8"SomeInt"_ns) == 1);
+
 		uniAttribute.Put(u8"SomeFloat"_ns, 1.0f);
+		CHECK(uniAttribute.Get<float>(u8"SomeFloat"_ns) == 1.0f);
 
 		const auto memoryStream = make_ref<natMemoryStream>(0, true, true, true);
 		uniAttribute.Encode(make_ref<natBinaryWriter>(memoryStream));
@@ -59,8 +63,48 @@ TEST_CASE("Jce", "[Jce]")
 			OldUniAttribute readAttribute{};
 			readAttribute.Decode(make_ref<natBinaryReader>(memoryStream));
 
-			CHECK(uniAttribute.Get<std::int32_t>(u8"SomeInt"_ns) == readAttribute.Get<std::int32_t>(u8"SomeInt"_ns));
-			CHECK(uniAttribute.Get<float>(u8"SomeFloat"_ns) == readAttribute.Get<float>(u8"SomeFloat"_ns));
+			CHECK(readAttribute.Get<std::int32_t>(u8"SomeInt"_ns) == 1);
+			CHECK(readAttribute.Get<float>(u8"SomeFloat"_ns) == 1.0f);
+		}
+	}
+
+	SECTION("Wup.UniPacket")
+	{
+		using namespace Wup;
+
+		UniPacket packet;
+
+		packet.GetAttribute().Put(u8"SomeInt"_ns, 1);
+		const auto test = make_ref<JceTest>();
+		test->SetTestFloat(2.0f);
+		test->SetTestInt(233);
+		test->GetTestMap()[1] = 2.0f;
+		packet.GetAttribute().Put(u8"JceTest"_ns, test);
+
+		packet.GetRequestPacket().SetsFuncName(u8"FuncName?"_nv);
+		packet.GetRequestPacket().SetsServantName(u8"ServantName?"_nv);
+
+		const auto memoryStream = make_ref<natMemoryStream>(0, true, true, true);
+		packet.Encode(make_ref<natBinaryWriter>(memoryStream));
+
+		memoryStream->SetPositionFromBegin(0);
+
+		{
+			UniPacket readPacket;
+			readPacket.Decode(make_ref<natBinaryReader>(memoryStream));
+
+			const auto& attribute = readPacket.GetAttribute();
+			CHECK(attribute.Get<std::int32_t>(u8"SomeInt"_ns) == 1);
+
+			const auto ptr = attribute.Get<natRefPointer<JceTest>>(u8"JceTest"_ns);
+			REQUIRE(ptr);
+			CHECK(ptr->GetTestFloat() == 2.0f);
+			CHECK(ptr->GetTestInt() == 233);
+			CHECK(ptr->GetTestMap()[1] == 2.0f);
+
+			const auto& requestPacket = readPacket.GetRequestPacket();
+			CHECK(requestPacket.GetsFuncName() == u8"FuncName?"_nv);
+			CHECK(requestPacket.GetsServantName() == u8"ServantName?"_nv);
 		}
 	}
 }

+ 35 - 1
YumeBot/Jce.cpp

@@ -374,6 +374,35 @@ bool JceInputStream::doRead(std::uint32_t tag, nString& value)
 	return false;
 }
 
+bool JceInputStream::doRead(std::uint32_t tag, gsl::span<std::uint8_t> const& value)
+{
+	if (SkipToTag(tag))
+	{
+		const auto[sizeField, sizeFieldSize] = ReadHead();
+		if (sizeField.Type != JceStruct::TypeEnum::Byte)
+		{
+			nat_Throw(JceDecodeException, u8"Type mismatch, got unexpected {0}."_nv, static_cast<std::uint32_t>(sizeField.Type));
+		}
+
+		std::uint8_t size;
+		if (!Read(0, size))
+		{
+			nat_Throw(JceDecodeException, u8"Read size failed."_nv);
+		}
+
+		if (static_cast<std::size_t>(value.size()) < size)
+		{
+			nat_Throw(JceDecodeException, u8"Span is not big enough."_nv);
+		}
+
+		m_Reader->GetUnderlyingStream()->ReadBytes(value.data(), size);
+
+		return true;
+	}
+
+	return false;
+}
+
 JceOutputStream::JceOutputStream(natRefPointer<natBinaryWriter> writer)
 	: m_Writer{ std::move(writer) }
 {
@@ -499,7 +528,7 @@ void JceOutputStream::doWrite(std::uint32_t tag, nString const& value)
 	doWrite(tag, value.GetView());
 }
 
-void JceOutputStream::doWrite(std::uint32_t tag, std::vector<std::uint8_t> const& value)
+void JceOutputStream::doWrite(std::uint32_t tag, gsl::span<const std::uint8_t> const& value)
 {
 	WriteHead({ tag, JceStruct::TypeEnum::SimpleList });
 	WriteHead({ 0, JceStruct::TypeEnum::Byte });
@@ -508,6 +537,11 @@ void JceOutputStream::doWrite(std::uint32_t tag, std::vector<std::uint8_t> const
 	m_Writer->GetUnderlyingStream()->WriteBytes(reinterpret_cast<ncData>(value.data()), size);
 }
 
+void JceOutputStream::doWrite(std::uint32_t tag, std::vector<std::uint8_t> const& value)
+{
+	doWrite(tag, gsl::make_span(value));
+}
+
 namespace
 {
 	template <template <typename> class Trait, typename T, typename Tuple>

+ 82 - 11
YumeBot/Jce.h

@@ -100,9 +100,24 @@ namespace YumeBot::Jce
 		void SkipField(JceStruct::TypeEnum type);
 		bool SkipToTag(std::uint32_t tag);
 
+		///	@brief	以指定的 tag 读取值,读取可能失败
+		///	@param	tag		指定 tag
+		///	@param	value	要写入的值
+		///	@return	读取是否成功
+		///	@remark 对于 JceStruct,若传入的引用指针为 const 限定的,则直接就地修改,否则将总是会创建新的实例并写入
+		///			这是由于新的 JceStruct 总是默认将引用指针初始化为空,而实际中未必总是需要新的实例引发的问题
+		///			若传入 JceStruct 派生的实例,也将就地修改
 		template <typename T>
 		[[nodiscard]] bool Read(std::uint32_t tag, T& value, Detail::NoneType = Detail::None)
 		{
+			const auto scope = NatsuLib::natScope([this, currentPos = m_Reader->GetUnderlyingStream()->GetPosition()]
+			{
+				if (std::uncaught_exceptions())
+				{
+					m_Reader->GetUnderlyingStream()->SetPositionFromBegin(currentPos);
+				}
+			});
+
 			return doRead(tag, value);
 		}
 
@@ -190,6 +205,8 @@ namespace YumeBot::Jce
 			return false;
 		}
 
+		bool doRead(std::uint32_t tag, gsl::span<std::uint8_t> const& value);
+
 		template <typename T>
 		bool doRead(std::uint32_t tag, std::vector<T>& value)
 		{
@@ -263,7 +280,51 @@ namespace YumeBot::Jce
 		}
 
 		template <typename T>
-		bool doRead(std::uint32_t tag, NatsuLib::natRefPointer<T>& value)
+		std::enable_if_t<std::is_base_of_v<JceStruct, T>, bool> doRead(std::uint32_t tag, NatsuLib::natRefPointer<T>& value)
+		{
+			if (SkipToTag(tag))
+			{
+				const auto[head, headSize] = ReadHead();
+				if (head.Type != JceStruct::TypeEnum::StructBegin)
+				{
+					nat_Throw(JceDecodeException, u8"Type mismatch, got unexpected {0}.", static_cast<std::uint32_t>(head.Type));
+				}
+
+				const auto newValue = NatsuLib::make_ref<T>();
+				JceDeserializer<T>::Deserialize(*this, *newValue);
+				value = newValue;
+
+				SkipToStructEnd();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		template <typename T>
+		std::enable_if_t<std::is_base_of_v<JceStruct, T>, bool> doRead(std::uint32_t tag, NatsuLib::natRefPointer<T> const& value)
+		{
+			if (SkipToTag(tag))
+			{
+				const auto[head, headSize] = ReadHead();
+				if (head.Type != JceStruct::TypeEnum::StructBegin)
+				{
+					nat_Throw(JceDecodeException, u8"Type mismatch, got unexpected {0}.", static_cast<std::uint32_t>(head.Type));
+				}
+
+				JceDeserializer<T>::Deserialize(*this, *value);
+
+				SkipToStructEnd();
+
+				return true;
+			}
+
+			return false;
+		}
+
+		template <typename T>
+		std::enable_if_t<std::is_base_of_v<JceStruct, T>, bool> doRead(std::uint32_t tag, T& value)
 		{
 			if (SkipToTag(tag))
 			{
@@ -273,7 +334,7 @@ namespace YumeBot::Jce
 					nat_Throw(JceDecodeException, u8"Type mismatch, got unexpected {0}.", static_cast<std::uint32_t>(head.Type));
 				}
 
-				value = JceDeserializer<T>::Deserialize(*this);
+				JceDeserializer<T>::Deserialize(*this, value);
 
 				SkipToStructEnd();
 
@@ -343,6 +404,7 @@ namespace YumeBot::Jce
 			}
 		}
 
+		void doWrite(std::uint32_t tag, gsl::span<const std::uint8_t> const& value);
 		void doWrite(std::uint32_t tag, std::vector<std::uint8_t> const& value);
 
 		template <typename T>
@@ -357,12 +419,23 @@ namespace YumeBot::Jce
 		}
 
 		template <typename T>
-		void doWrite(std::uint32_t tag, NatsuLib::natRefPointer<T> const& value)
+		std::enable_if_t<std::is_base_of_v<JceStruct, T>> doWrite(std::uint32_t tag, T const& value)
 		{
 			WriteHead({ tag, JceStruct::TypeEnum::StructBegin });
 			JceSerializer<T>::Serialize(*this, value);
 			WriteHead({ 0, JceStruct::TypeEnum::StructEnd });
 		}
+
+		template <typename T>
+		std::enable_if_t<std::is_base_of_v<JceStruct, T>> doWrite(std::uint32_t tag, NatsuLib::natRefPointer<T> const& value)
+		{
+			if (!value)
+			{
+				nat_Throw(JceEncodeException, u8"value is nullptr.");
+			}
+
+			doWrite(tag, *value);
+		}
 	};
 
 	struct NoOp
@@ -461,8 +534,8 @@ namespace YumeBot::Jce
 // 读取 optional 的时候不会返回 false
 #define FIELD(name, tag, type, ...) \
 			{\
-				using FieldType = typename Utility::MayRemoveTemplate<Utility::RemoveCvRef<decltype(ret->Get##name())>, std::optional>::Type;\
-				if (!stream.Read(tag, ret->Get##name(),\
+				using FieldType = typename Utility::MayRemoveTemplate<Utility::RemoveCvRef<decltype(value.Get##name())>, std::optional>::Type;\
+				if (!stream.Read(tag, value.Get##name(),\
 					Utility::ReturnFirst<Utility::ConcatTrait<Utility::ConcatTrait<Utility::RemoveCvRef, Utility::BindTrait<std::is_same,\
 						Detail::NoneType>::Result>::Result, std::negation>::Result, Detail::NoneType>(__VA_ARGS__)))\
 				{\
@@ -474,25 +547,23 @@ namespace YumeBot::Jce
 	template <>\
 	struct JceDeserializer<name>\
 	{\
-		static NatsuLib::natRefPointer<name> Deserialize(JceInputStream& stream)\
-		{\
-			auto ret = NatsuLib::make_ref<name>();
+		static void Deserialize(JceInputStream& stream, name& value)\
+		{
 
 #define END_JCE_STRUCT(name) \
-			return ret;\
 		}\
 	};
 
 #include "JceStructDef.h"
 
 
-#define FIELD(name, tag, type, ...) stream.Write(tag, value->Get##name());
+#define FIELD(name, tag, type, ...) stream.Write(tag, value.Get##name());
 
 #define JCE_STRUCT(name, alias) \
 	template <>\
 	struct JceSerializer<name>\
 	{\
-		static void Serialize(JceOutputStream& stream, NatsuLib::natRefPointer<name> const& value)\
+		static void Serialize(JceOutputStream& stream, name const& value)\
 		{
 
 #define END_JCE_STRUCT(name) \

+ 63 - 0
YumeBot/Wup.cpp

@@ -30,3 +30,66 @@ void OldUniAttribute::Decode(natRefPointer<natBinaryReader> const& reader)
 		nat_Throw(natErrException, NatErr_InternalErr, u8"Data is corrupted"_nv);
 	}
 }
+
+UniPacket::UniPacket()
+	: m_OldRespIRet{}
+{
+}
+
+void UniPacket::Encode(natRefPointer<natBinaryWriter> const& writer)
+{
+	const auto tmpBuffer = make_ref<natMemoryStream>(0, false, true, true);
+	m_UniAttribute.Encode(make_ref<natBinaryWriter>(tmpBuffer));
+	const auto buffer = tmpBuffer->GetInternalBuffer();
+	m_RequestPacket.GetsBuffer().assign(buffer, buffer + tmpBuffer->GetSize());
+
+	const auto underlyingStream = writer->GetUnderlyingStream();
+	const auto sizePos = underlyingStream->GetPosition();
+	underlyingStream->SetPosition(NatSeek::Cur, 4);
+
+	JceOutputStream os{ writer };
+	os.Write(0, m_RequestPacket);
+	const auto endPos = underlyingStream->GetPosition();
+	const auto length = endPos - sizePos;
+	underlyingStream->SetPositionFromBegin(sizePos);
+	writer->WritePod(static_cast<std::int32_t>(length));
+	underlyingStream->SetPositionFromBegin(endPos);
+}
+
+void UniPacket::Decode(natRefPointer<natBinaryReader> const& reader)
+{
+	reader->GetUnderlyingStream()->SetPosition(NatSeek::Cur, 4);
+	JceInputStream is{ reader };
+	if (!is.Read(0, m_RequestPacket))
+	{
+		nat_Throw(natErrException, NatErr_InternalErr, u8"Read RequestPacket failed."_nv);
+	}
+
+	const auto& buffer = m_RequestPacket.GetsBuffer();
+	m_UniAttribute.Decode(make_ref<natBinaryReader>(make_ref<natExternMemoryStream>(buffer.data(), buffer.size(), true)));
+}
+
+UniPacket UniPacket::CreateResponse()
+{
+	UniPacket result;
+	result.m_RequestPacket.SetiRequestId(m_RequestPacket.GetiRequestId());
+	result.m_RequestPacket.SetsServantName(m_RequestPacket.GetsServantName());
+	result.m_RequestPacket.SetsFuncName(m_RequestPacket.GetsFuncName());
+	result.m_RequestPacket.SetiVersion(m_RequestPacket.GetiVersion());
+	return result;
+}
+
+void UniPacket::CreateOldRespEncode(JceOutputStream& os)
+{
+	const auto memoryStream = make_ref<natMemoryStream>(0, false, true, true);
+	const auto tempWriter = make_ref<natBinaryWriter>(memoryStream);
+	m_UniAttribute.Encode(tempWriter);
+
+	os.Write(1, m_RequestPacket.GetiVersion());
+	os.Write(2, m_RequestPacket.GetcPacketType());
+	os.Write(3, m_RequestPacket.GetiRequestId());
+	os.Write(4, m_RequestPacket.GetiMessageType());
+	os.Write(5, m_OldRespIRet);
+	os.Write(6, gsl::make_span(memoryStream->GetInternalBuffer(), memoryStream->GetSize()));
+	os.Write(7, m_RequestPacket.Getstatus());
+}

+ 43 - 0
YumeBot/Wup.h

@@ -68,6 +68,12 @@ namespace YumeBot::Jce::Wup
 
 #define JCE_STRUCT(name, alias) \
 		constexpr nStrView GetName(ImplicitConvertibleIdentityType<NatsuLib::natRefPointer<name>>) noexcept\
+		{\
+			using namespace NatsuLib::StringLiterals;\
+			return u8 ## alias ## _nv;\
+		}\
+		\
+		constexpr nStrView GetName(NatsuLib::natRefPointer<name> const&) noexcept\
 		{\
 			using namespace NatsuLib::StringLiterals;\
 			return u8 ## alias ## _nv;\
@@ -168,4 +174,41 @@ namespace YumeBot::Jce::Wup
 	private:
 		std::unordered_map<nString, std::unordered_map<nString, std::vector<std::uint8_t>>> m_Data;
 	};
+
+	class UniPacket
+	{
+	public:
+		UniPacket();
+
+		void Encode(NatsuLib::natRefPointer<NatsuLib::natBinaryWriter> const& writer);
+		void Decode(NatsuLib::natRefPointer<NatsuLib::natBinaryReader> const& reader);
+
+		UniPacket CreateResponse();
+		void CreateOldRespEncode(JceOutputStream& os);
+
+		RequestPacket& GetRequestPacket() noexcept
+		{
+			return m_RequestPacket;
+		}
+
+		OldUniAttribute& GetAttribute() noexcept
+		{
+			return m_UniAttribute;
+		}
+
+		std::int32_t GetOldRespIRet() const noexcept
+		{
+			return m_OldRespIRet;
+		}
+
+		void SetOldRespIRet(std::int32_t value) noexcept
+		{
+			m_OldRespIRet = value;
+		}
+
+	private:
+		RequestPacket m_RequestPacket;
+		OldUniAttribute m_UniAttribute;
+		std::int32_t m_OldRespIRet;
+	};
 }