Code:
//
// Copyright (c) CptSky <[Only registered and activated users can see links. Click Here To Register...]>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the organization nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
using System;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using COServer.Core.Extensions;
namespace COServer.Protocol
{
public enum Channel : ushort
{
None = 0,
Normal = 2000,
Private = 2001,
Action = 2002,
Team = 2003,
Syndicate = 2004,
System = 2005,
Family = 2006,
Talk = 2007,
Yelp = 2008,
Friend = 2009,
Global = 2010,
GM = 2011,
Whisper = 2012,
Ghost = 2013,
Serve = 2014,
Register = 2100,
Entrance = 2101,
Shop = 2102,
PetTalk = 2103,
CryOut = 2104,
WebPage = 2105,
NewMessage = 2106,
Task = 2107,
SynWar_First = 2108,
SynWar_Next = 2109,
LeaveWord = 2110,
SynAnnounce = 2111,
MessageBox = 2112,
Reject = 2113,
SynTenet = 2114,
MsgTrade = 2201,
MsgFriend = 2202,
MsgTeam = 2203,
MsgSyn = 2204,
MsgOther = 2205,
MsgSystem = 2206,
Broadcast = 2500,
};
public enum Style : ushort
{
None = 0,
Flash = 2,
Blast = 8,
FlashAndBlast = 10,
};
public sealed unsafe class MsgTalk : Msg<MsgTalk>
{
public static readonly int Type = 1004;
public const int MaxNameLength = 15;
public const int MaxWordsLength = 255;
private const int MaxStringPackLength = 320;
private const int SpeakerStringIdx = 0;
private const int HearerStringIdx = 1;
private const int EmotionStringIdx = 2;
private const int WordsStringIdx = 3;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct MsgInfo
{
public MsgHeader Header;
public int Color;
public ushort Channel;
public ushort Style;
public int Time;
public uint Look1;
public uint Look2;
public fixed byte StringPack[MaxStringPackLength]; // Speaker, Hearer, Emotion, Words
};
private MsgInfo msg;
public Color Color
{
get => Color.FromArgb(msg.Color);
set => msg.Color = value.ToArgb();
}
public Channel Channel
{
get => (Channel)msg.Channel;
set => msg.Channel = (ushort)value;
}
public Style Style
{
get => (Style)msg.Style;
set => msg.Style = (ushort)value;
}
public int Time
{
get => msg.Time;
set => msg.Time = value;
}
public string Speaker
{
get
{
fixed (MsgInfo* pinnedMsg = &msg)
{
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
return packer[SpeakerStringIdx];
}
}
}
public string Hearer
{
get
{
fixed (MsgInfo* pinnedMsg = &msg)
{
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
return packer[HearerStringIdx];
}
}
}
public string Emotion
{
get
{
fixed (MsgInfo* pinnedMsg = &msg)
{
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
return packer[EmotionStringIdx];
}
}
}
public string Words
{
get
{
fixed (MsgInfo* pinnedMsg = &msg)
{
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
return packer[WordsStringIdx];
}
}
}
public static MsgTalk Create(string speaker, string hearer, string words, Channel channel, Color color)
{
var msg = Create();
msg.Color = color;
msg.Channel = channel;
msg.Style = Style.None;
msg.Time = Environment.TickCount;
msg.ResetStringPack();
msg.PushString(speaker);
msg.PushString(hearer);
msg.PushString(String.Empty);
msg.PushString(words);
return msg;
}
private void ResetStringPack()
{
// reset count to zero
msg.StringPack[0] = 0;
}
private void PushString(string str)
{
fixed (MsgInfo* pinnedMsg = &msg)
{
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
packer.Push(str);
}
}
protected override bool CopyTo(Span<byte> buf)
{
// ensure the header is properly updated
fixed (MsgInfo* pinnedMsg = &msg)
{
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
msg.Header.Type = (ushort)Type;
msg.Header.Length = (ushort)(sizeof(MsgInfo) - MaxStringPackLength + packer.Length);
}
fixed (MsgInfo* pinnedMsg = &msg)
{
var msgData = new Span<byte>(pinnedMsg, sizeof(MsgInfo));
return msgData.TryCopyTo(buf);
}
}
protected override bool CopyFrom(ReadOnlySpan<byte> buf)
{
fixed (MsgInfo* pinnedMsg = &msg)
{
var msgData = new Span<byte>(pinnedMsg, sizeof(MsgInfo));
var stringPack = new Span<byte>(pinnedMsg->StringPack, MaxStringPackLength);
var packer = new StringPacker(stringPack);
return buf.TryCopyTo(msgData) && packer.Count == 4;
}
}
}
}