code upload

codigo original de f4mp y tilted para referencias
This commit is contained in:
Jous99 2026-01-06 18:45:00 +01:00
parent e60a59f058
commit 37b16f1547
1787 changed files with 174425 additions and 0 deletions

2
f4mp_originalcode/.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

View file

@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: Genetical
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Files**
Please attach the following files
- config.txt (Documents\My Games\Fallout4\F4MP\config.txt)
- F4MP.log (Documents\My Games\Fallout4\F4MP\F4MP.log)
- server_config.txt (Fallout 4\Data\f4mp_bin\server_config.txt)
**Load Order**
```
Please provide your full load order here
```
**Additional context**
Add any other context about the problem here.

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

57
f4mp_originalcode/.gitignore vendored Normal file
View file

@ -0,0 +1,57 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.pdb
*.log
*.tlog
*.cache
*.pyc
.vs/
f4se/
f4se_common/
f4se_loader/
f4se_loader_common/
f4se_steam_loader/
x64/
x64_vc11/
xbyak/
f4mp/scripts/.vscode/.ropeproject/config.py
f4mp/scripts/.vscode/.ropeproject/objectdb
f4mp/scripts/.vscode/settings.json
f4mp/scripts/copy.bat
f4mp_server/scripts/bin

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,17 @@
#pragma once
namespace f4mp
{
namespace animations
{
extern const UCHAR idle[];
extern const UCHAR jogForward[];
extern const UCHAR jogForwardRight[];
extern const UCHAR jogRight[];
extern const UCHAR jogBackwardRight[];
extern const UCHAR jogBackward[];
extern const UCHAR jogBackwardLeft[];
extern const UCHAR jogLeft[];
extern const UCHAR jogForwardLeft[];
}
}

View file

@ -0,0 +1,502 @@
#include "Animator.h"
#include "Animations.h"
#include "Player.h"
#include "f4se/NiNodes.h"
#include <fstream>
std::vector<std::vector<std::string>> f4mp::Animator::animatedNodes;
std::vector<std::unordered_map<std::string, UInt32>> f4mp::Animator::animatedNodeIndices;
std::vector<std::string> f4mp::Animator::stateNames;
std::unordered_map<std::string, SInt32> f4mp::Animator::stateIDs;
std::unordered_map<std::string, f4mp::Animator::Animation> f4mp::Animator::animations;
f4mp::Animator::Animator(Type type) : type(type), startTime(zpl_time_now())
{
Play(&animations["idle"]);
}
void f4mp::Animator::Play(const Animation* newAnimation)
{
animation = newAnimation;
startTime = zpl_time_now();
}
size_t f4mp::Animator::GetAnimatedNodeCount() const
{
return animatedNodes[type].size();
}
bool f4mp::Animator::IsAnimatedNode(const std::string& nodeName) const
{
return animatedNodeIndices[type].count(nodeName) != 0;
}
const std::string& f4mp::Animator::GetNodeName(UInt32 nodeIndex) const
{
return animatedNodes[type][nodeIndex];
}
UInt32 f4mp::Animator::GetNodeIndex(const std::string& nodeName) const
{
auto foundNode = animatedNodeIndices[type].find(nodeName);
if (foundNode == animatedNodeIndices[type].end())
{
return (UInt32)-1;
}
return foundNode->second;
}
const f4mp::Animator::Animation* f4mp::Animator::GetAnimation() const
{
return animation;
}
std::vector<f4mp::Animator::Transform> f4mp::Animator::GetTransforms() const
{
if (!animation)
{
return {};
}
// TODO: non-looping animations
float t = fmod(zpl_time_now() - startTime, animation->duration);
size_t frame = 0;
while (animation->frames[frame].duration < t)
{
t -= animation->frames[frame].duration;
frame++;
if (frame >= animation->frames.size())
{
return {};
}
}
const Frame& prevFrame = animation->frames[frame];
const Frame& nextFrame = animation->frames[(frame + 1) % animation->frames.size()];
t /= prevFrame.duration;
std::vector<Transform> curFrame(animation->nodes.size());
for (size_t node = 0; node < curFrame.size(); node++)
{
Transform& curTransform = curFrame[node];
const Transform& prevTransform = prevFrame.transforms[node];
const Transform& nextTransform = nextFrame.transforms[node];
zpl_vec3_lerp(&curTransform.position, prevTransform.position, nextTransform.position, t);
zpl_quat_slerp_approx(&curTransform.rotation, prevTransform.rotation, nextTransform.rotation, t);
curTransform.scale = zpl_lerp(prevTransform.scale, nextTransform.scale, t);
}
return curFrame;
}
bool f4mp::Animator::ForEachNode(TESObjectREFR* ref, const std::function<bool(NiNode*, UInt32)>& callback)
{
if (!ref)
{
return true;
}
NiNode* root = ref->GetActorRootNode(false);
if (!root)
{
return true;
}
return root->Visit([&](NiAVObject* obj)
{
NiNode* node = DYNAMIC_CAST(obj, NiAVObject, NiNode);
if (!node)
{
return false;
}
UInt32 nodeIndex = GetNodeIndex(node->m_name.c_str());
if (nodeIndex >= GetAnimatedNodeCount())
{
return false;
}
return callback(node, nodeIndex);
});
}
bool f4mp::Animator::Save(const std::string& path, const Animation& animation, bool binary) const
{
std::ofstream file(path);
if (!file)
{
return false;
}
std::vector<UCHAR> data;
if (binary)
{
const size_t nodes = animation.nodes.size(), frames = animation.frames.size();
data.insert(data.end(), (UCHAR*)(&animation.loops), (UCHAR*)(&animation.loops + 1));
data.insert(data.end(), (UCHAR*)(&nodes), (UCHAR*)(&nodes + 1));
data.insert(data.end(), (UCHAR*)(&frames), (UCHAR*)(&frames + 1));
}
else
{
file << animation.loops << ' ' << animation.nodes.size() << ' ' << animation.frames.size() << std::endl;
}
for (const std::string& nodeName : animation.nodes)
{
if (binary)
{
const size_t length = nodeName.length();
data.insert(data.end(), (UCHAR*)(&length), (UCHAR*)(&length + 1));
data.insert(data.end(), (UCHAR*)(&nodeName[0]), (UCHAR*)(&nodeName[0] + length));
}
else
{
file << nodeName << ' ';
}
}
if (binary)
{
file << std::hex;
for (UCHAR byte : data)
{
file << "0x" << (UINT)byte << ", ";
}
data.clear();
}
else
{
file << std::endl;
}
for (const Frame& frame : animation.frames)
{
if (binary)
{
data.insert(data.end(), (UCHAR*)(&frame.duration), (UCHAR*)(&frame.duration + 1));
}
else
{
file << frame.duration << ' ';
}
for (const Transform& transform : frame.transforms)
{
if (binary)
{
data.insert(data.end(), (UCHAR*)(&transform), (UCHAR*)(&transform + 1));
}
else
{
file << transform.position.x << ' ' << transform.position.y << ' ' << transform.position.z << ' ';
file << transform.rotation.x << ' ' << transform.rotation.y << ' ' << transform.rotation.z << ' ' << transform.rotation.w << ' ';
file << transform.scale << ' ';
}
}
file << std::endl;
if (binary)
{
for (UCHAR byte : data)
{
file << "0x" << (UINT)byte << ", ";
}
data.clear();
}
}
return true;
}
f4mp::Animator::Animation f4mp::Animator::Load(const std::string& path) const
{
std::ifstream file(path);
if (!file)
{
return Animation(-1.f);
}
size_t nodes, frames;
std::string nodeName;
Animation animation;
file >> animation.loops >> nodes >> frames;
animation.nodes.resize(nodes);
animation.frames.resize(frames);
for (std::string& nodeName : animation.nodes)
{
file >> nodeName;
}
for (Frame& frame : animation.frames)
{
file >> frame.duration;
animation.duration += frame.duration;
frame.transforms.resize(nodes);
for (Transform& transform : frame.transforms)
{
file >> transform.position.x >> transform.position.y >> transform.position.z;
file >> transform.rotation.x >> transform.rotation.y >> transform.rotation.z >> transform.rotation.w;
file >> transform.scale;
}
}
return animation;
}
void f4mp::Animator::OnClientUpdate(const Player& player)
{
const Animation* newAnimation = &animations["idle"];
int walkDir = player.GetWalkDir(player.GetDisplacement().xy, player.GetLookAngle());
if (walkDir >= 0)
{
const Animation* walkAnimations[]
{
&animations["jog_forward"],
&animations["jog_forward_right"],
&animations["jog_right"],
&animations["jog_backward_right"],
&animations["jog_backward"],
&animations["jog_backward_left"],
&animations["jog_left"],
&animations["jog_forward_left"],
};
newAnimation = walkAnimations[walkDir];
}
if (animation != newAnimation)
{
Play(newAnimation);
}
}
f4mp::Animator::Animation f4mp::Animator::Load(const UCHAR data[])
{
const UCHAR* curData = data;
Animation animation;
animation.loops = *(((const bool*&)curData)++);
const size_t nodes = *(((const size_t*&)curData)++), frames = *(((const size_t*&)curData)++);
animation.nodes.resize(nodes);
animation.frames.resize(frames);
for (std::string& nodeName : animation.nodes)
{
const size_t length = *(((const size_t*&)curData)++);
nodeName = std::string((const char*)curData, (const char*)curData + length);
curData += length;
}
size_t frameCount = 0;
for (Frame& frame : animation.frames)
{
frame.duration = *(((const float*&)curData)++);
if (frame.duration <= 0)
{
break;
}
animation.duration += frame.duration;
frame.transforms.resize(nodes);
for (Transform& transform : frame.transforms)
{
transform = *(((const Transform*&)curData)++);
}
frameCount++;
}
animation.frames.resize(frameCount);
return animation;
}
void f4mp::Animator::Init()
{
animations["idle"] = Load(animations::idle);
animations["jog_forward"] = Load(animations::jogForward);
animations["jog_forward_left"] = Load(animations::jogForwardLeft);
animations["jog_forward_right"] = Load(animations::jogForwardRight);
animations["jog_backward"] = Load(animations::jogBackward);
animations["jog_backward_left"] = Load(animations::jogBackwardLeft);
animations["jog_backward_right"] = Load(animations::jogBackwardRight);
animations["jog_left"] = Load(animations::jogLeft);
animations["jog_right"] = Load(animations::jogRight);
if (stateNames.size() == 0)
{
animatedNodes =
{
// Human
{
"skeleton.nif" ,
"COM" ,
"Pelvis" ,
"LLeg_Thigh" ,
"LLeg_Calf" ,
"LLeg_Foot" ,
"LLeg_Toe1" ,
"RLeg_Thigh" ,
"RLeg_Calf" ,
"RLeg_Foot" ,
"RLeg_Toe1" ,
"SPINE1" ,
"SPINE2" ,
"Chest" ,
"LArm_Collarbone",
"LArm_UpperArm" ,
"LArm_ForeArm1" ,
"LArm_ForeArm2" ,
"LArm_ForeArm3" ,
"LArm_Hand" ,
"LArm_Finger11" ,
"LArm_Finger12" ,
"LArm_Finger13" ,
"LArm_Finger21" ,
"LArm_Finger22" ,
"LArm_Finger23" ,
"LArm_Finger31" ,
"LArm_Finger32" ,
"LArm_Finger33" ,
"LArm_Finger41" ,
"LArm_Finger42" ,
"LArm_Finger43" ,
"LArm_Finger51" ,
"LArm_Finger52" ,
"LArm_Finger53" ,
"Neck" ,
"Head" ,
"RArm_Collarbone",
"RArm_UpperArm" ,
"RArm_ForeArm1" ,
"RArm_ForeArm2" ,
"RArm_ForeArm3" ,
"RArm_Hand" ,
"RArm_Finger11" ,
"RArm_Finger12" ,
"RArm_Finger13" ,
"RArm_Finger21" ,
"RArm_Finger22" ,
"RArm_Finger23" ,
"RArm_Finger31" ,
"RArm_Finger32" ,
"RArm_Finger33" ,
"RArm_Finger41" ,
"RArm_Finger42" ,
"RArm_Finger43" ,
"RArm_Finger51" ,
"RArm_Finger52" ,
"RArm_Finger53" ,
}
};
for (const std::vector<std::string>& allowedNodesInType : animatedNodes)
{
std::unordered_map<std::string, UInt32> allowedNodeIndicesInType;
for (const std::string& allowedNode : allowedNodesInType)
{
allowedNodeIndicesInType[allowedNode] = allowedNodeIndicesInType.size();
}
animatedNodeIndices.push_back(allowedNodeIndicesInType);
}
stateNames =
{
"None",
"JogForward",
"JogBackward",
"JogLeft",
"JogRight",
"JumpUp",
"JumpFall",
"JumpLand",
"FireWeapon"
};
for (SInt32 i = 0; i < static_cast<SInt32>(stateNames.size()); i++)
{
const std::string& state = stateNames[i];
std::string lowerState(state.length(), '\0');
for (size_t i = 0; i < state.length(); i++)
{
lowerState[i] = tolower(state[i]);
}
stateIDs[lowerState] = i;
}
}
}
const std::string& f4mp::Animator::GetStateName(SInt32 id)
{
if (id < 0 || id >= stateNames.size())
{
id = 0;
}
return stateNames[id];
}
SInt32 f4mp::Animator::GetStateID(const std::string& name)
{
auto id = stateIDs.find(Lower(name));
if (id == stateIDs.end())
{
return -1;
}
return id->second;
}
bool f4mp::Animator::Loops(SInt32 id)
{
std::string animStateName = GetStateName(id);
if (animStateName == "None")
{
return true;
}
if (animStateName.find("Jog") != std::string::npos)
{
return true;
}
// TOOD: add more
return false;
}
f4mp::Animator::Animation::Animation(float duration) : loops(true), duration(duration)
{
}

View file

@ -0,0 +1,86 @@
#pragma once
#include "client.h"
namespace f4mp
{
class Player;
class Animator
{
public:
enum Type
{
Human
};
struct Transform
{
zpl_vec3 position;
zpl_quat rotation;
float scale;
};
struct Frame
{
float duration;
std::vector<Transform> transforms;
};
struct Animation
{
bool loops;
float duration;
std::vector<std::string> nodes;
std::vector<Frame> frames;
Animation(float duration = 0.f);
};
private:
static std::vector<std::vector<std::string>> animatedNodes;
static std::vector<std::unordered_map<std::string, UInt32>> animatedNodeIndices;
static std::vector<std::string> stateNames;
static std::unordered_map<std::string, SInt32> stateIDs;
static std::unordered_map<std::string, Animation> animations;
Type type;
const Animation* animation;
double startTime;
public:
Animator(Type type);
void Play(const Animation* newAnimation);
size_t GetAnimatedNodeCount() const;
bool IsAnimatedNode(const std::string& nodeName) const;
const std::string& GetNodeName(UInt32 nodeIndex) const;
UInt32 GetNodeIndex(const std::string& nodeName) const;
const Animation* GetAnimation() const;
std::vector<Transform> GetTransforms() const;
bool ForEachNode(TESObjectREFR* ref, const std::function<bool(NiNode*, UInt32)>& callback);
bool Save(const std::string& path, const Animation& animation, bool binary = true) const;
Animation Load(const std::string& path) const;
void OnClientUpdate(const Player& player);
static void Init();
static Animation Load(const UCHAR data[]);
static const std::string& GetStateName(SInt32 id);
static SInt32 GetStateID(const std::string& name);
static bool Loops(SInt32 id);
};
}

View file

@ -0,0 +1,259 @@
#include "Character.h"
#include "f4mp.h"
#include "f4se/NiNodes.h"
f4mp::Character::Character()
{
animator = std::make_unique<Animator>(Animator::Human);
}
f4mp::Animator& f4mp::Character::GetAnimator()
{
return *animator;
}
const f4mp::Animator& f4mp::Character::GetAnimator() const
{
return *animator;
}
void f4mp::Character::OnEntityUpdate(librg_event* event)
{
Entity::OnEntityUpdate(event, F4MP::GetInstance().player.get() != this);
std::vector<float> transforms;
double syncTime;
Utils::Read(event->data, transforms);
Utils::Read(event->data, syncTime);
if (transforms.size() == 0)
{
return;
}
float deltaTime = syncTime - transformBuffer.syncTime;
if (deltaTime < 1e-2f)
{
//transformBuffer.syncTime = syncTime;
return;
}
TransformBuffer newTransformBuffer(transforms.size() / 8, syncTime, zpl_time_now(), deltaTime);
for (size_t i = 0; i < newTransformBuffer.next.size(); i++)
{
size_t ti = i * 8;
Transform& transform = newTransformBuffer.next[i];
transform.position = { transforms[ti], transforms[ti + 1], transforms[ti + 2] };
transform.rotation = { transforms[ti + 3], transforms[ti + 4], transforms[ti + 5], transforms[ti + 6] };
transform.scale = transforms[ti + 7];
float mag = zpl_quat_mag(transform.rotation);
if (isnan(mag) || fabsf(mag - 1.f) > 1e-2f)
{
transform.rotation = { 0, 0, 0, 0 };
}
}
if (transformBuffer.prev.size() == 0)
{
newTransformBuffer.prev = newTransformBuffer.next;
}
float t = min((newTransformBuffer.time - transformBuffer.time) / transformBuffer.deltaTime, 1.f);
for (size_t nodeIndex = 0; nodeIndex < transformBuffer.prev.size(); nodeIndex++)
{
const Transform& prevTransform = transformBuffer.prev[nodeIndex];
const Transform& nextTransform = transformBuffer.next[nodeIndex];
Transform& curTransform = newTransformBuffer.prev[nodeIndex];
if (zpl_quat_dot(newTransformBuffer.next[nodeIndex].rotation, nextTransform.rotation) < 0.f)
{
newTransformBuffer.next[nodeIndex].rotation *= -1.f;
}
zpl_vec3_lerp(&curTransform.position, prevTransform.position, nextTransform.position, t);
zpl_quat_slerp_approx(&curTransform.rotation, prevTransform.rotation, nextTransform.rotation, t);
curTransform.scale = zpl_lerp(prevTransform.scale, nextTransform.scale, t);
}
while (lock.test_and_set(std::memory_order_acquire));
transformBuffer = newTransformBuffer;
lock.clear(std::memory_order_release);
}
void f4mp::Character::OnClientUpdate(librg_event* event)
{
Entity::OnClientUpdate(event);
std::vector<float> transforms;
TESObjectREFR* ref = GetRef();
if (ref)
{
NiNode* root = ref->GetActorRootNode(false);
if (root)
{
transforms.resize(animator->GetAnimatedNodeCount() * 8);
root->Visit([&](NiAVObject* obj)
{
NiNode* node = dynamic_cast<NiNode*>(obj);
if (!node)
{
return false;
}
UInt32 nodeIndex = animator->GetNodeIndex(node->m_name.c_str());
if (nodeIndex >= animator->GetAnimatedNodeCount())
{
return false;
}
UInt32 index = nodeIndex * 8;
const NiMatrix43 rot = node->m_localTransform.rot;
zpl_mat4 mat
{
rot.data[0][0], rot.data[0][1], rot.data[0][2], rot.data[0][3],
rot.data[1][0], rot.data[1][1], rot.data[1][2], rot.data[1][3],
rot.data[2][0], rot.data[2][1], rot.data[2][2], rot.data[2][3],
};
zpl_quat quat;
zpl_quat_from_mat4(&quat, &mat);
transforms[index + 0] = node->m_localTransform.pos.x;
transforms[index + 1] = node->m_localTransform.pos.y;
transforms[index + 2] = node->m_localTransform.pos.z;
transforms[index + 3] = quat.x;
transforms[index + 4] = quat.y;
transforms[index + 5] = quat.z;
transforms[index + 6] = quat.w;
transforms[index + 7] = node->m_localTransform.scale;
return false;
});
if (ref->GetObjectRootNode() != root)
{
// TODO: temporary
const Animator::Animation* animation = animator->GetAnimation();
if (animation)
{
std::vector<Animator::Transform> animationTransforms = animator->GetTransforms();
for (size_t i = 0; i < animationTransforms.size(); i++)
{
const Animator::Transform& transform = animationTransforms[i];
size_t index = animator->GetNodeIndex(animation->nodes[i]) * 8;
transforms[index + 0] = transform.position.x;
transforms[index + 1] = transform.position.y;
transforms[index + 2] = transform.position.z;
transforms[index + 3] = transform.rotation.x;
transforms[index + 4] = transform.rotation.y;
transforms[index + 5] = transform.rotation.z;
transforms[index + 6] = transform.rotation.w;
transforms[index + 7] = transform.scale;
}
}
}
}
}
transformBuffer.syncTime = zpl_time_now();
Utils::Write(event->data, transforms);
Utils::Write(event->data, transformBuffer.syncTime);
}
void f4mp::Character::OnTick()
{
F4MP& f4mp = F4MP::GetInstance();
if (this == f4mp.player.get())
{
return;
}
while (lock.test_and_set(std::memory_order_acquire));
TransformBuffer transforms = transformBuffer;
lock.clear(std::memory_order_release);
f4mp.task->AddTask(new Task([=]()
{
TESObjectREFR* ref = GetRef();
if (!ref)
{
return;
}
NiNode* root = ref->GetActorRootNode(false);
if (!root)
{
return;
}
float t = min((zpl_time_now() - transforms.time) / transforms.deltaTime, 1.f);
root->Visit([&](NiAVObject* obj)
{
NiNode* node = dynamic_cast<NiNode*>(obj);
if (!node)
{
return false;
}
UInt32 nodeIndex = animator->GetNodeIndex(node->m_name.c_str());
if (nodeIndex >= transforms.prev.size())
{
return false;
}
const Transform& prevTransform = transforms.prev[nodeIndex];
const Transform& nextTransform = transforms.next[nodeIndex];
Transform curTransform;
zpl_vec3_lerp(&curTransform.position, prevTransform.position, nextTransform.position, t);
zpl_quat_slerp_approx(&curTransform.rotation, prevTransform.rotation, nextTransform.rotation, t);
curTransform.scale = zpl_lerp(prevTransform.scale, nextTransform.scale, t);
node->m_localTransform.pos = (NiPoint3&)curTransform.position;
node->m_localTransform.scale = curTransform.scale;
if (fabsf(zpl_quat_mag(nextTransform.rotation) - 1.f) > 1e-2f || fabsf(zpl_quat_mag(prevTransform.rotation) - 1.f) > 1e-2f)
{
return false;
}
zpl_mat4 rot;
zpl_mat4_from_quat(&rot, curTransform.rotation);
zpl_float4* m = zpl_float44_m(&rot);
node->m_localTransform.rot =
{
m[0][0], m[0][1], m[0][2], m[0][3],
m[1][0], m[1][1], m[1][2], m[1][3],
m[2][0], m[2][1], m[2][2], m[2][3],
};
return false;
});
NiAVObject::NiUpdateData updateData;
root->UpdateWorldData(&updateData);
}));
}
f4mp::Character::TransformBuffer::TransformBuffer() : TransformBuffer(0, -1.0, -1.0, 0.f)
{
}
f4mp::Character::TransformBuffer::TransformBuffer(size_t transforms, double syncTime, double time, float deltaTime) : prev(transforms), next(transforms), syncTime(syncTime), time(time), deltaTime(deltaTime)
{
}

View file

@ -0,0 +1,49 @@
#pragma once
#include "Entity.h"
#include "Animator.h"
#include <memory>
#include <atomic>
namespace f4mp
{
class Character : public Entity
{
public:
struct Transform
{
zpl_vec3 position;
zpl_quat rotation;
float scale;
};
struct TransformBuffer
{
std::vector<Transform> prev, next;
double syncTime, time;
float deltaTime;
TransformBuffer();
TransformBuffer(size_t transforms, double syncTime, double time, float deltaTime);
};
Character();
Animator& GetAnimator();
const Animator& GetAnimator() const;
void OnEntityUpdate(librg_event* event) override;
void OnClientUpdate(librg_event* event) override;
void OnTick() override;
private:
std::unique_ptr<Animator> animator;
std::atomic_flag lock = ATOMIC_FLAG_INIT;
TransformBuffer transformBuffer;
};
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "client.h"
#include <queue>
namespace f4mp
{
class Clone
{
private:
std::queue<PlayerData> records;
};
}

View file

@ -0,0 +1,185 @@
#include "Entity.h"
#include "Player.h"
#include "NPC.h"
#include "f4mp.h"
#include "f4se/NiNodes.h"
f4mp::Entity* f4mp::Entity::Get(librg_event* event)
{
return Get(event->entity);
}
f4mp::Entity* f4mp::Entity::Get(librg_entity* entity)
{
if (entity == nullptr)
{
return nullptr;
}
return (Entity*)entity->user_data;
}
f4mp::Entity::Entity() : entity(nullptr), refFormID((UInt32)-1)
{
SetNumber("angleX", 0.f);
SetNumber("angleY", 0.f);
SetNumber("angleZ", 0.f);
}
f4mp::Entity::~Entity()
{
}
f4mp::Entity* f4mp::Entity::Create(librg_event* event)
{
Entity* entity = nullptr;
switch (event->entity->type)
{
case EntityType::Player:
entity = new Player();
break;
case EntityType::NPC:
entity = new NPC();
break;
}
entity->entity = event->entity;
event->entity->user_data = entity;
entity->OnEntityCreate(event);
return entity;
}
void f4mp::Entity::OnConnectRequest(librg_event* event)
{
}
void f4mp::Entity::OnConnectAccept(librg_event* event)
{
// HACK: weird?
entity = event->entity;
}
void f4mp::Entity::OnDisonnect(librg_event* event)
{
event->entity->user_data = nullptr;
}
void f4mp::Entity::OnEntityCreate(librg_event* event)
{
}
void f4mp::Entity::OnEntityUpdate(librg_event* event)
{
OnEntityUpdate(event, true);
}
void f4mp::Entity::OnEntityRemove(librg_event* event)
{
delete this;
event->entity->user_data = nullptr;
}
void f4mp::Entity::OnClientUpdate(librg_event* event)
{
TESObjectREFR* ref = GetRef();
if (ref)
{
entity->position = (zpl_vec3&)ref->pos;
SetNumber("angleX", ToDegrees(ref->rot.x));
SetNumber("angleY", ToDegrees(ref->rot.y));
SetNumber("angleZ", ToDegrees(ref->rot.z));
}
librg_data_wf32(event->data, GetNumber("angleX"));
librg_data_wf32(event->data, GetNumber("angleY"));
librg_data_wf32(event->data, GetNumber("angleZ"));
}
void f4mp::Entity::OnTick()
{
}
const librg_entity* f4mp::Entity::GetNetworkEntity() const
{
return entity;
}
librg_entity* f4mp::Entity::GetNetworkEntity()
{
return entity;
}
UInt32 f4mp::Entity::GetRefFormID() const
{
return refFormID;
}
TESObjectREFR* f4mp::Entity::GetRef()
{
//return ref;
return dynamic_cast<TESObjectREFR*>(LookupFormByID(refFormID));
}
void f4mp::Entity::SetRef(TESObjectREFR* ref)
{
refFormID = ref->formID;
//this->ref = ref;
/*NiNode* root = ref->GetActorRootNode(false);
if (root)
{
printf("-----------\n");
root->Visit([&](NiAVObject* obj)
{
printf("%s\n", obj->m_name.c_str());
return false;
});
printf("-----------\n");
}*/
}
Float32 f4mp::Entity::GetNumber(const std::string& name) const
{
// HACK: horrible
return numbers.find(name)->second;
}
void f4mp::Entity::SetNumber(const std::string& name, Float32 number)
{
if (F4MP::GetInstance().player.get() == this)
{
for (auto& instance : F4MP::instances)
{
instance->player->numbers[name] = number;
}
return;
}
numbers[name] = number;
}
void f4mp::Entity::OnEntityUpdate(librg_event* event, bool syncTransform)
{
zpl_vec3 angles{ librg_data_rf32(event->data), librg_data_rf32(event->data), librg_data_rf32(event->data) };
SetNumber("angleX", angles.x);
SetNumber("angleY", angles.y);
SetNumber("angleZ", angles.z);
if (syncTransform)
{
TESObjectREFR* ref = GetRef();
F4MP::SyncTransform(ref, entity->position, angles, true);
}
}

View file

@ -0,0 +1,68 @@
#pragma once
#include "client.h"
namespace f4mp
{
class Entity
{
public:
static Entity* Get(librg_event* event);
static Entity* Get(librg_entity* entity);
template<class T>
static T* GetAs(librg_event* event);
template<class T>
static T* GetAs(librg_entity* entity);
public:
Entity();
virtual ~Entity();
static Entity* Create(librg_event* event);
virtual void OnConnectRequest(librg_event* event);
virtual void OnConnectAccept(librg_event* event);
virtual void OnDisonnect(librg_event* event);
virtual void OnEntityCreate(librg_event* event);
virtual void OnEntityUpdate(librg_event* event);
virtual void OnEntityRemove(librg_event* event);
virtual void OnClientUpdate(librg_event* event);
virtual void OnTick();
const librg_entity* GetNetworkEntity() const;
librg_entity* GetNetworkEntity();
UInt32 GetRefFormID() const;
TESObjectREFR* GetRef();
void SetRef(TESObjectREFR* ref);
Float32 GetNumber(const std::string& name) const;
void SetNumber(const std::string& name, Float32 number);
protected:
virtual void OnEntityUpdate(librg_event* event, bool syncTransform);
private:
librg_entity* entity;
//TESObjectREFR* ref;
UInt32 refFormID;
std::unordered_map<std::string, Float32> numbers;
};
template<class T>
inline T* Entity::GetAs(librg_event* event)
{
return dynamic_cast<T*>(Get(event));
}
template<class T>
inline T* Entity::GetAs(librg_entity* entity)
{
return dynamic_cast<T*>(Get(entity));
}
}

View file

@ -0,0 +1,42 @@
#include "NPC.h"
#include "f4mp.h"
f4mp::NPC::NPC() : formID(0), ownerEntityID((UInt32)-1)
{
}
void f4mp::NPC::OnEntityCreate(librg_event* event)
{
Character::OnEntityCreate(event);
UInt32 entityID = GetNetworkEntity()->id;
formID = librg_data_ri32(event->data);
ownerEntityID = librg_data_ri32(event->data);
F4MP& f4mp = F4MP::GetInstance();
f4mp.entityIDs[formID] = entityID;
_MESSAGE("OnSpawnEntity: %u(%x)", entityID, formID);
TESObjectREFR* gameEntity = DYNAMIC_CAST(LookupFormByID(formID), TESForm, TESObjectREFR);
if (!gameEntity)
{
return;
}
SetRef(gameEntity);
printf("%u %x\n", entityID, formID);
}
void f4mp::NPC::OnEntityUpdate(librg_event* event)
{
Character::OnEntityUpdate(event);
}
void f4mp::NPC::OnClientUpdate(librg_event* event)
{
Character::OnClientUpdate(event);
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "Character.h"
namespace f4mp
{
class NPC : public Character
{
public:
NPC();
void OnEntityCreate(librg_event* event) override;
void OnEntityUpdate(librg_event* event) override;
void OnClientUpdate(librg_event* event) override;
private:
UInt32 formID, ownerEntityID;
};
}

View file

@ -0,0 +1,408 @@
#include "Player.h"
#include "f4mp.h"
#include "f4se/NiNodes.h"
void f4mp::Player::OnConnect(Actor* player, TESNPC* playerActorBase)
{
SetRef(player);
appearance.Fill(playerActorBase);
wornItems.Fill(player);
}
void f4mp::Player::OnEntityCreate(librg_event* event)
{
Character::OnEntityCreate(event);
Utils::Read(event->data, appearance.female);
Utils::Read(event->data, appearance.weights);
Utils::Read(event->data, appearance.hairColor);
Utils::Read(event->data, appearance.headParts);
Utils::Read(event->data, appearance.morphSetValue);
Utils::Read(event->data, appearance.morphRegionData1);
Utils::Read(event->data, appearance.morphRegionData2);
Utils::Read(event->data, appearance.morphSetData1);
Utils::Read(event->data, appearance.morphSetData2);
Utils::Read(event->data, wornItems.data1);
Utils::Read(event->data, wornItems.data2);
for (const auto& item : wornItems.data2)
{
_MESSAGE("%s", item.c_str());
}
auto& npcs = (*g_dataHandler)->arrNPC_;
for (UInt32 i = 0; i < npcs.count; i++)
{
if (strcmp(npcs[i]->fullName.name.c_str(), "F4MP Player") == 0)
{
SetAppearance(npcs[i], appearance);
break;
}
}
struct Tmp
{
UInt32 id;
const WornItemsData& wornItems;
} tmp{ event->entity->id, GetWornItems() };
F4MP& self = F4MP::GetInstance();
self.papyrus->GetExternalEventRegistrations("OnEntityCreate", &tmp, [](UInt64 handle, const char* scriptName, const char* callbackName, void* dataPtr)
{
Tmp* tmp = static_cast<Tmp*>(dataPtr);
VMArray<TESForm*> wornItems(F4MP::DecodeWornItems(tmp->wornItems));
SendPapyrusEvent2<UInt32, VMArray<TESForm*>>(handle, scriptName, callbackName, tmp->id, wornItems);
});
}
void f4mp::Player::OnEntityUpdate(librg_event* event)
{
Character::OnEntityUpdate(event);
SetNumber("health", librg_data_rf32(event->data));
SetAnimStateID(librg_data_ri32(event->data));
//transformDeltaTime = transformDeltaTime * transformDeltaTimeInertia + (curTransformTime - prevTransformTime) * (1.f - transformDeltaTimeInertia);
}
void f4mp::Player::OnEntityRemove(librg_event* event)
{
Character::OnEntityRemove(event);
}
void f4mp::Player::OnClientUpdate(librg_event* event)
{
Character::OnClientUpdate(event);
displacement = event->entity->position - prevPosition;
GetAnimator().OnClientUpdate(*this);
// TODO: move this to the Animation class.
const std::string& animState = GetAnimState();
const char* newAnimState;
switch (GetWalkDir(displacement.xy, GetLookAngle()))
{
case 0:
newAnimState = "JogForward";
break;
case 1:
case 2:
case 3:
newAnimState = "JogRight";
break;
case 4:
newAnimState = "JogBackward";
break;
case 5:
case 6:
case 7:
newAnimState = "JogLeft";
break;
default:
newAnimState = "None";
}
SetAnimState(newAnimState);
prevPosition = event->entity->position;
librg_data_wf32(event->data, GetNumber("health"));
librg_data_wi32(event->data, GetAnimStateID());
}
void f4mp::Player::OnTick()
{
Character::OnTick();
}
SInt32 f4mp::Player::GetInteger(const std::string& name) const
{
// HACK: horrible
return integers.find(name)->second;
}
void f4mp::Player::SetInteger(const std::string& name, SInt32 integer)
{
if (F4MP::GetInstance().player.get() == this)
{
for (auto& instance : F4MP::instances)
{
instance->player->integers[name] = integer;
}
return;
}
integers[name] = integer;
}
SInt32 f4mp::Player::GetAnimStateID() const
{
return GetInteger("animState");
}
const std::string& f4mp::Player::GetAnimState() const
{
return Animator::GetStateName(GetAnimStateID());
}
float f4mp::Player::GetLookAngle() const
{
return GetNumber("angleZ");
}
void f4mp::Player::SetAnimStateID(SInt32 id)
{
SetInteger("animState", id);
}
void f4mp::Player::SetAnimState(const std::string& name)
{
SetAnimStateID(Animator::GetStateID(name));
}
UInt32 f4mp::Player::GetEntityID() const
{
return entityID;
}
const f4mp::client::AppearanceData& f4mp::Player::GetAppearance() const
{
return appearance;
}
const f4mp::client::WornItemsData& f4mp::Player::GetWornItems() const
{
return wornItems;
}
const zpl_vec3& f4mp::Player::GetDisplacement() const
{
return displacement;
}
void f4mp::Player::OnConnectRequest(librg_event* event)
{
Character::OnConnectRequest(event);
Utils::Write(event->data, appearance.female);
Utils::Write(event->data, appearance.weights);
Utils::Write(event->data, appearance.hairColor);
Utils::Write(event->data, appearance.headParts);
Utils::Write(event->data, appearance.morphSetValue);
Utils::Write(event->data, appearance.morphRegionData1);
Utils::Write(event->data, appearance.morphRegionData2);
Utils::Write(event->data, appearance.morphSetData1);
Utils::Write(event->data, appearance.morphSetData2);
Utils::Write(event->data, wornItems.data1);
Utils::Write(event->data, wornItems.data2);
}
void f4mp::Player::OnConnectAccept(librg_event* event)
{
Character::OnConnectAccept(event);
entityID = event->entity->id;
event->entity->user_data = this;
}
void f4mp::Player::OnDisonnect(librg_event* event)
{
Character::OnDisonnect(event);
}
int f4mp::Player::GetWalkDir(const zpl_vec2& displacement, float lookAngle)
{
const float walkThreshold = 1.f;
float speed = zpl_vec2_mag(displacement);
if (speed < walkThreshold)
{
return -1;
}
float angle = atan2(displacement.x, displacement.y) - zpl_to_radians(lookAngle) + ZPL_TAU / 16.f;
angle = angle - floor(angle / ZPL_TAU) * ZPL_TAU;
return (int)floor(angle / ZPL_TAU * 8.f);
}
void f4mp::Player::SetAppearance(TESNPC* actorBase, const AppearanceData& appearance)
{
//sex
//HACK: i don't know if it's legit or not
if (appearance.female)
{
actorBase->actorData.flags |= TESActorBaseData::kFlagFemale;
}
else
{
actorBase->actorData.flags &= ~TESActorBaseData::kFlagFemale;
}
//HACK: there might be some memory leaks going around that might cause horrible problems. i'm sorry i don't know better..
if (actorBase->headData)
{
Heap_Free(actorBase->headData);
actorBase->headData = nullptr;
}
if (actorBase->headParts)
{
Heap_Free(actorBase->headParts);
actorBase->headParts = nullptr;
}
if (actorBase->morphSetValue)
{
Heap_Free(actorBase->morphSetValue);
actorBase->morphSetValue = nullptr;
}
if (actorBase->morphRegionData)
{
Heap_Free(actorBase->morphRegionData);
actorBase->morphRegionData = nullptr;
}
if (actorBase->morphSetData)
{
Heap_Free(actorBase->morphSetData);
actorBase->morphSetData = nullptr;
}
if (actorBase->tints)
{
Heap_Free(actorBase->tints);
actorBase->tints = nullptr;
}
actorBase->headData = new TESNPC::HeadData();
tArray<BGSColorForm*>& colors = (*g_dataHandler)->arrCLFM;
UInt32 j;
for (j = 0; j < colors.count; j++)
{
if (appearance.hairColor.compare(colors[j]->fullName.name.c_str()) == 0)
{
actorBase->headData->hairColor = colors[j];
break;
}
}
if (j == colors.count)
{
_MESSAGE("hairColor: %s", appearance.hairColor.c_str());
}
//TODO: texture sync
//dest->headData->faceTextures = src->headData->faceTextures;
actorBase->weightThin = appearance.weights[0];
actorBase->weightMuscular = appearance.weights[1];
actorBase->weightLarge = appearance.weights[2];
actorBase->numHeadParts = appearance.headParts.size();
actorBase->headParts = (BGSHeadPart**)Heap_Allocate(sizeof(BGSHeadPart*) * actorBase->numHeadParts);
for (UInt8 i = 0; i < actorBase->numHeadParts; i++)
{
tArray<BGSHeadPart*>& headParts = (*g_dataHandler)->arrHDPT;
for (j = 0; j < headParts.count; j++)
{
if (appearance.headParts[i].compare(headParts[j]->partName.c_str()) == 0)
{
actorBase->headParts[i] = headParts[j];
break;
}
}
if (j == headParts.count)
{
_MESSAGE("hairPart: %s", appearance.headParts[i].c_str());
}
}
actorBase->morphSetValue = new tArray<float>();
for (UInt32 i = 0; i < appearance.morphSetValue.size(); i++)
{
actorBase->morphSetValue->Push(appearance.morphSetValue[i]);
}
actorBase->morphRegionData = new tHashSet<TESNPC::FaceMorphRegion, UInt32>();
/*for(const auto& region : appearance.morphRegionData)
{
TESNPC::FaceMorphRegion tmp;
tmp.index = std::get<0>(region);
std::copy(std::get<1>(region).begin(), std::get<1>(region).end(), tmp.value);
actorBase->morphRegionData->Add(&tmp);
}*/
for (size_t i = 0; i < appearance.morphRegionData1.size(); i++)
{
TESNPC::FaceMorphRegion tmp;
tmp.index = appearance.morphRegionData1[i];
std::copy(appearance.morphRegionData2[i].begin(), appearance.morphRegionData2[i].end(), tmp.value);
actorBase->morphRegionData->Add(&tmp);
}
actorBase->morphSetData = new tHashSet<TESNPC::MorphSetData, UInt32>();
for (size_t i = 0; i < appearance.morphSetData1.size(); i++)
{
TESNPC::MorphSetData tmp;
tmp.key = appearance.morphSetData1[i];
tmp.value = appearance.morphSetData2[i];
actorBase->morphSetData->Add(&tmp);
}
/*for (const auto& data : appearance.morphSetData)
{
TESNPC::MorphSetData tmp;
tmp.key = std::get<0>(data);
tmp.value = std::get<1>(data);
actorBase->morphSetData->Add(&tmp);
}*/
//TODO: tint sync
//dest->tints = src->tints;
}
void f4mp::Player::SetWornItems(Actor* actor, const WornItemsData& wornItems)
{
F4MP& self = F4MP::GetInstance();
struct Args
{
Actor* actor;
const WornItemsData& wornItems;
} args{ actor, wornItems };
self.papyrus->GetExternalEventRegistrations("OnCopyWornItems", &args, [](UInt64 handle, const char* scriptName, const char* callbackName, void* dataPtr)
{
Args* args = static_cast<Args*>(dataPtr);
VMArray<TESForm*> itemsToWear(F4MP::DecodeWornItems(args->wornItems));
SendPapyrusEvent2<Actor*, VMArray<TESForm*>>(handle, scriptName, callbackName, args->actor, itemsToWear);
});
}
f4mp::Player::Player() : entityID((UInt32)-1)
{
SetAnimStateID(0);
SetNumber("health", 1.f);
}

View file

@ -0,0 +1,61 @@
#pragma once
#include "Character.h"
#include <memory>
namespace f4mp
{
class Player : public Character
{
private:
UInt32 entityID;
zpl_vec3 prevPosition;
zpl_vec3 displacement;
client::AppearanceData appearance;
client::WornItemsData wornItems;
// TODO: might wanna move them to the Entity class.
// NOTE: in that case, fix the ~EntVar~ functions to also work with the Entity class.
std::unordered_map<std::string, SInt32> integers;
public:
static int GetWalkDir(const zpl_vec2& displacement, float lookAngle);
static void SetAppearance(TESNPC* actorBase, const AppearanceData& appearance);
static void SetWornItems(Actor* actor, const WornItemsData& wornItems);
Player();
void OnConnect(Actor* player, TESNPC* playerActorBase);
void OnConnectRequest(librg_event* event) override;
void OnConnectAccept(librg_event* event) override;
void OnDisonnect(librg_event* event) override;
void OnEntityCreate(librg_event* event) override;
void OnEntityUpdate(librg_event* event) override;
void OnEntityRemove(librg_event* event) override;
void OnClientUpdate(librg_event* event) override;
void OnTick() override;
SInt32 GetInteger(const std::string& name) const;
void SetInteger(const std::string& name, SInt32 integer);
SInt32 GetAnimStateID() const;
const std::string& GetAnimState() const;
void SetAnimStateID(SInt32 id);
void SetAnimState(const std::string& name);
UInt32 GetEntityID() const;
const client::AppearanceData& GetAppearance() const;
const client::WornItemsData& GetWornItems() const;
const zpl_vec3& GetDisplacement() const;
float GetLookAngle() const;
};
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,6 @@
#pragma once
namespace f4mp
{
extern UInt32 topicInfoIDs[68227];
}

View file

@ -0,0 +1,126 @@
#pragma once
#include "f4se/PapyrusNativeFunctions.h"
#include "f4se/PluginAPI.h"
#include "f4se/PapyrusEvents.h"
#include "f4se/GameThreads.h"
#include "f4se/GameObjects.h"
#include "f4se/GameForms.h"
#include "f4se/GameFormComponents.h"
#include "f4se/GameData.h"
#include "f4se/GameReferences.h"
#include "f4se/GameExtraData.h"
#include "f4se/GameRTTI.h"
#include "common.h"
#include <unordered_map>
namespace f4mp
{
template<class T>
T ToDegrees(const T& radians)
{
return radians * 360.0f / ZPL_TAU;
}
namespace client
{
struct AppearanceData : public f4mp::AppearanceData
{
void Fill(TESNPC* actorBase)
{
Clear();
female = CALL_MEMBER_FN(actorBase, GetSex)() == 1;
weights = { actorBase->weightThin, actorBase->weightMuscular, actorBase->weightLarge };
hairColor = actorBase->headData->hairColor->fullName.name;
for (UInt8 i = 0; i < actorBase->numHeadParts; i++)
{
headParts.push_back(actorBase->headParts[i]->partName.c_str());
}
for (UInt32 i = 0; i < actorBase->morphSetValue->count; i++)
{
morphSetValue.push_back((*actorBase->morphSetValue)[i]);
}
actorBase->morphRegionData->ForEach([=](TESNPC::FaceMorphRegion* region)
{
//morphRegionData.push_back(std::make_tuple(region->index, std::vector<f32>(&region->value[0], &region->value[8])));
morphRegionData1.push_back(region->index);
morphRegionData2.push_back(std::vector<f32>(&region->value[0], &region->value[8]));
return true;
});
actorBase->morphSetData->ForEach([&](TESNPC::MorphSetData* data)
{
//morphSetData.push_back(std::make_tuple(data->key, data->value));
morphSetData1.push_back(data->key);
morphSetData2.push_back(data->value);
return true;
});
}
};
struct WornItemsData : public f4mp::WornItemsData
{
void Fill(Actor* actor)
{
Clear();
for (UInt32 i = 0; i < ActorEquipData::kMaxSlots; i++)
{
TESForm* item = actor->equipData->slots[i].item;
if (item)
{
/*if (std::find_if(data.begin(), data.end(), [&](const std::tuple<u8, std::string>& wornItem)
{
return std::get<0>(wornItem) == item->formType && std::get<1>(wornItem).compare(item->GetFullName()) == 0;
}) == data.end())
{
data.push_back(std::make_pair(item->formType, item->GetFullName()));
}*/
size_t j;
for (j = 0; j < data1.size(); j++)
{
if (data1[j] == item->formType && data2[j].compare(item->GetFullName()) == 0)
{
break;
}
}
if (j == data1.size())
{
data1.push_back(item->formType);
data2.push_back(item->GetFullName());
}
}
}
}
};
}
class Task : public ITaskDelegate
{
public:
Task(const std::function<void()>& callback) : callback(callback)
{
}
void Run() override
{
callback();
}
std::function<void()> callback;
};
struct Config
{
std::string hostAddress;
};
}

View file

@ -0,0 +1,6 @@
#include "common.h"
u64 f4mp::GetUniqueFormID(u32 ownerEntityID, u32 entityFormID)
{
return ((u64)ownerEntityID << 32) | entityFormID;
}

View file

@ -0,0 +1,306 @@
#pragma once
#include <librg.h>
#include <vector>
#include <string>
#include <tuple>
namespace f4mp
{
struct MessageType
{
enum : u16
{
Hit = LIBRG_EVENT_LAST + 1u,
FireWeapon,
SpawnEntity,
SyncEntity,
SpawnBuilding,
RemoveBuilding,
Speak
};
};
struct EntityType
{
enum : u32
{
Player = 0,
NPC
};
};
struct HitData
{
u32 hitter, hittee;
f32 damage;
};
struct SpawnEntityData
{
u32 formID;
zpl_vec3 position;
zpl_vec3 angles;
u32 entityID;
u32 ownerEntityID;
};
struct SyncEntityData
{
u32 formID;
zpl_vec3 position;
zpl_vec3 angles;
f64 syncedTime;
};
struct SpawnBuildingData
{
u32 ownerEntityID;
u32 formID;
u32 baseFormID; // HACK: 0 if it's just a transform update
zpl_vec3 position;
zpl_vec3 angles;
};
struct RemoveBuildingData
{
u64 uniqueFormID;
};
struct TransformData
{
u32 formID;
zpl_vec3 position;
zpl_vec3 angles;
};
struct SpeakData
{
u32 clientEntityID;
u32 speakerFormID; // 0 if client
u32 topicInfoFormID;
};
struct AppearanceData
{
bool female;
std::vector<f32> weights;
std::string hairColor;
std::vector<std::string> headParts;
std::vector<f32> morphSetValue;
std::vector<u32> morphRegionData1;
std::vector<std::vector<f32>> morphRegionData2;
std::vector<u32> morphSetData1;
std::vector<f32> morphSetData2;
void Clear()
{
headParts.clear();
morphSetValue.clear();
morphRegionData1.clear();
morphRegionData2.clear();
morphSetData1.clear();
morphSetData2.clear();
}
};
struct WornItemsData
{
std::vector<u8> data1;
std::vector<std::string> data2;
void Clear()
{
//data.clear();
data1.clear();
data2.clear();
}
};
// TODO: the ones for tuple seem to be broken
class Utils
{
private:
template<class T>
static void WriteForTuple(librg_data* data, const T& value)
{
Write(data, value);
}
template<class T, class... Ts>
static void WriteForTuple(librg_data* data, const T& value, Ts... values)
{
Write(data, value);
WriteForTuple(data, values...);
}
template<class T, size_t... I>
static void WriteTuple(librg_data* data, const T& tuple, std::index_sequence<I...>)
{
WriteForTuple(data, std::get<I>(tuple)...);
}
template<class T>
static void ReadForTuple(librg_data* data, T& value)
{
Read(data, value);
}
template<class T, class... Ts>
static void ReadForTuple(librg_data* data, T& value, Ts... values)
{
Read(data, value);
ReadForTuple(data, values...);
}
template<class T, size_t... I>
static void ReadTuple(librg_data* data, T& tuple, std::index_sequence<I...>)
{
ReadForTuple(data, std::get<I>(tuple)...);
}
public:
template<class T>
static void Write(librg_data* data, const T& value);
template<class T>
static void Write(librg_data* data, const std::vector<T>& values)
{
//_MESSAGE("size: %u", values.size());
librg_data_wu32(data, static_cast<u32>(values.size()));
for (const T& value : values)
{
Write(data, value);
}
}
template<class... Ts>
static void Write(librg_data* data, const std::tuple<Ts...>& values)
{
static constexpr auto size = std::tuple_size<std::tuple<Ts...>>::value;
WriteTuple(data, values, std::make_index_sequence<size>{});
}
static void Write(librg_data* data, const bool& value)
{
//_MESSAGE("%s", value ? "true" : "false");
librg_data_wb8(data, value);
}
static void Write(librg_data* data, const u8& value)
{
//_MESSAGE("%u", value);
librg_data_wu8(data, value);
}
static void Write(librg_data* data, const u32& value)
{
//_MESSAGE("%u", value);
librg_data_wu32(data, value);
}
static void Write(librg_data* data, const f32& value)
{
//_MESSAGE("%f", value);
librg_data_wf32(data, value);
}
static void Write(librg_data* data, const f64& value)
{
librg_data_wf64(data, value);
}
static void Write(librg_data* data, const std::string& value)
{
//_MESSAGE("%s", value.c_str());
librg_data_wu32(data, static_cast<u32>(value.size()));
for (char ch : value)
{
librg_data_wb8(data, ch);
}
}
template<class T>
static void Read(librg_data* data, T& value);
template<class T>
static void Read(librg_data* data, std::vector<T>& values)
{
values.resize(librg_data_ru32(data));
for (T& value : values)
{
Read(data, value);
}
}
template<class... Ts>
static void Read(librg_data* data, std::tuple<Ts...>& values)
{
static constexpr auto size = std::tuple_size<std::tuple<Ts...>>::value;
ReadTuple(data, values, std::make_index_sequence<size>{});
}
static void Read(librg_data* data, bool& value)
{
value = !!librg_data_rb8(data);
}
static void Read(librg_data* data, u8& value)
{
value = librg_data_ru8(data);
}
static void Read(librg_data* data, u32& value)
{
value = librg_data_ru32(data);
}
static void Read(librg_data* data, f32& value)
{
value = librg_data_rf32(data);
}
static void Read(librg_data* data, f64& value)
{
value = librg_data_rf64(data);
}
static void Read(librg_data* data, std::string& value)
{
value.resize(librg_data_ru32(data));
for (char& ch : value)
{
ch = librg_data_rb8(data);
}
}
};
template<class T>
std::string Lower(const T& string)
{
std::string lower = string;
for (size_t i = 0; i < lower.length(); i++)
{
lower[i] = tolower(lower[i]);
}
return lower;
}
u64 GetUniqueFormID(u32 ownerEntityID, u32 entityFormID);
}

View file

@ -0,0 +1,4 @@
LIBRARY "f4mp"
EXPORTS
F4SEPlugin_Query
F4SEPlugin_Load

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,156 @@
#include "client.h"
#include "Player.h"
#include <librg.h>
#include <memory>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <functional>
#include <algorithm>
#include <atomic>
// TODO: completely separate static functions and member variable access with instance function copies of them.
namespace f4mp
{
struct SyncDataForPapyrus
{
TESObjectREFR* ref;
zpl_vec3 position;
zpl_vec3 angles;
};
class Topic : public TESForm
{
public:
enum { kTypeID = kFormType_DIAL };
UInt8 unkTopic1[48];
TESForm** infos;
UInt8 unkTopic2[8];
UInt32 count;
UInt32 count2;
UInt8 unkTopic3[16];
};
class F4MP
{
friend class Entity;
friend class Character;
friend class Player;
friend class NPC;
public:
static F4MP& GetInstance();
static std::string GetPath();
F4MP();
virtual ~F4MP();
bool Init(const F4SEInterface* f4se);
librg_entity* FetchEntity(UInt32 id, const std::string& errorMsg = "no entity with ID '%d'!");
static std::vector<TESForm*> DecodeWornItems(const WornItemsData& wornItems);
static void SyncTransform(TESObjectREFR* ref, zpl_vec3 position, zpl_vec3 angles, bool ignoreAngleXY = false);
static void TranslateTo(TESObjectREFR* ref, zpl_vec3 position, zpl_vec3 angles, Float32 speed, Float32 rotSpeed);
static void MoveTo(TESObjectREFR* ref, zpl_vec3 position, zpl_vec3 angles);
private:
static std::vector<std::unique_ptr<F4MP>> instances;
static size_t activeInstance, nextActiveInstance;
Config config;
std::string address;
SInt32 port;
librg_ctx ctx;
PluginHandle handle;
F4SEMessagingInterface* messaging;
F4SEPapyrusInterface* papyrus;
F4SETaskInterface* task;
F4SEObjectInterface* object;
std::unique_ptr<Player> player;
std::unordered_map<UInt32, UInt32> entityIDs;
std::unordered_map<UInt64, TransformData> buildings;
std::unordered_set<UInt32> knownBuildings;
std::unordered_map<UInt32, std::unordered_multiset<UInt32>> linesToSpeak;
std::list<UInt32> topicInfoRemainders;
Topic* topicInstance;
static void OnConnectRequest(librg_event* event);
static void OnConnectAccept(librg_event* event);
static void OnConnectRefuse(librg_event* event);
static void OnDisonnect(librg_event* event);
//TODO: abstract event handlers as they're basically the same
static void OnEntityCreate(librg_event* event);
static void OnEntityUpdate(librg_event* event);
static void OnEntityRemove(librg_event* event);
static void OnClientUpdate(librg_event* event);
static void OnHit(librg_message* msg);
static void OnFireWeapon(librg_message* msg);
static void OnSyncEntity(librg_message* msg);
static void OnSpawnBuilding(librg_message* msg);
static void OnRemoveBuilding(librg_message* msg);
static void OnSpeak(librg_message* msg);
static UInt32 GetClientInstanceID(StaticFunctionTag* base);
static void SetClient(StaticFunctionTag* base, UInt32 instance);
static bool IsConnected(StaticFunctionTag* base);
static bool Connect(StaticFunctionTag* base, Actor* player, TESNPC* playerActorBase, BSFixedString address, SInt32 port);
static bool Disconnect(StaticFunctionTag* base);
static void Tick(StaticFunctionTag* base);
static void SyncWorld(StaticFunctionTag* base);
static UInt32 GetPlayerEntityID(StaticFunctionTag* base);
static UInt32 GetEntityID(StaticFunctionTag* base, TESObjectREFR* ref);
static void SetEntityRef(StaticFunctionTag* base, UInt32 entityID, TESObjectREFR* ref);
static bool IsEntityValid(StaticFunctionTag* base, UInt32 entityID);
static VMArray<Float32> GetEntityPosition(StaticFunctionTag* base, UInt32 entityID);
static void SetEntityPosition(StaticFunctionTag* base, UInt32 entityID, float x, float y, float z);
//TODO: abstract ...EntVar...
static void SetEntVarNum(StaticFunctionTag* base, UInt32 entityID, BSFixedString name, Float32 value);
static void SetEntVarAnim(StaticFunctionTag* base, UInt32 entityID, BSFixedString animState);
static Float32 GetEntVarNum(StaticFunctionTag* base, UInt32 entityID, BSFixedString name);
static BSFixedString GetEntVarAnim(StaticFunctionTag* base, UInt32 entityID);
static VMArray<TESObjectREFR*> GetRefsInCell(StaticFunctionTag* base, TESObjectCELL* cell);
static Float32 Atan2(StaticFunctionTag* base, Float32 y, Float32 x);
static BSFixedString GetWalkDir(StaticFunctionTag* base, Float32 dX, Float32 dY, Float32 angleZ);
static bool AnimLoops(StaticFunctionTag* base, BSFixedString animState);
static void CopyAppearance(StaticFunctionTag* base, TESNPC* src, TESNPC* dest);
static void CopyWornItems(StaticFunctionTag* base, Actor* src, Actor* dest);
static void PlayerHit(StaticFunctionTag* base, UInt32 hitter, UInt32 hittee, Float32 damage);
static void PlayerFireWeapon(StaticFunctionTag* base);
};
}

View file

@ -0,0 +1,238 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{F021FA50-A159-46F9-8455-B079C458E5AF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>f4mp</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;F4MP_EXPORTS;_WINDOWS;_USRDLL;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\..;$(SolutionDir)\thirdparty\enet\include;$(SolutionDir)\thirdparty\zpl\code;$(SolutionDir)\thirdparty\librg\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ForcedIncludeFiles>common/IPrefix.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
</Link>
<PostBuildEvent>
<Command>copy "$(TargetPath)" "D:\SteamLibrary\steamapps\common\Fallout 4\Data\F4SE\Plugins\$(TargetFileName)"
copy "$(ProjectDir)\scripts\F4MP.psc" "D:\SteamLibrary\steamapps\common\Fallout 4\Data\Scripts\Source\User\F4MP.psc"
copy "$(ProjectDir)\scripts\F4MPFirePoint.psc" "D:\SteamLibrary\steamapps\common\Fallout 4\Data\Scripts\Source\User\F4MPFirePoint.psc"
copy "$(ProjectDir)\scripts\F4MPPlayer.psc" "D:\SteamLibrary\steamapps\common\Fallout 4\Data\Scripts\Source\User\F4MPPlayer.psc"
copy "$(ProjectDir)\scripts\F4MPQuest.psc" "D:\SteamLibrary\steamapps\common\Fallout 4\Data\Scripts\Source\User\F4MPQuest.psc"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;F4MP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;F4MP_EXPORTS;_WINDOWS;_USRDLL;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\..;$(SolutionDir)\thirdparty\enet\include;$(SolutionDir)\thirdparty\zpl\code;$(SolutionDir)\thirdparty\librg\include</AdditionalIncludeDirectories>
<ForcedIncludeFiles>common/IPrefix.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
</Link>
<PostBuildEvent>
<Command>copy "$(TargetPath)" "D:\SteamLibrary\steamapps\common\Fallout 4\Data\F4SE\Plugins\$(TargetFileName)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;F4MP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\common_vc11.vcxproj">
<Project>{d4c128a1-73dc-4941-a453-ce55af239ba8}</Project>
</ProjectReference>
<ProjectReference Include="..\f4se\f4se.vcxproj">
<Project>{a236f69d-8ff9-4491-ac5f-45bf49448bbe}</Project>
</ProjectReference>
<ProjectReference Include="..\f4se_common\f4se_common.vcxproj">
<Project>{20c6411c-596f-4b85-be4e-8bc91f59d8a6}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\f4se\GameAPI.cpp" />
<ClCompile Include="..\f4se\GameData.cpp" />
<ClCompile Include="..\f4se\GameEvents.cpp" />
<ClCompile Include="..\f4se\GameExtraData.cpp" />
<ClCompile Include="..\f4se\GameFormComponents.cpp" />
<ClCompile Include="..\f4se\GameForms.cpp" />
<ClCompile Include="..\f4se\GameObjects.cpp" />
<ClCompile Include="..\f4se\GameReferences.cpp" />
<ClCompile Include="..\f4se\GameRTTI.cpp" />
<ClCompile Include="..\f4se\GameTypes.cpp" />
<ClCompile Include="..\f4se\GameUtilities.cpp" />
<ClCompile Include="..\f4se\NiObjects.cpp" />
<ClCompile Include="..\f4se\NiTypes.cpp" />
<ClCompile Include="..\f4se\PapyrusArgs.cpp" />
<ClCompile Include="..\f4se\PapyrusEvents.cpp" />
<ClCompile Include="..\f4se\PapyrusForm.cpp" />
<ClCompile Include="..\f4se\PapyrusInterfaces.cpp" />
<ClCompile Include="..\f4se\PapyrusValue.cpp" />
<ClCompile Include="..\f4se\PapyrusVM.cpp" />
<ClCompile Include="Animations.cpp" />
<ClCompile Include="Animator.cpp" />
<ClCompile Include="Character.cpp" />
<ClCompile Include="common.cpp" />
<ClCompile Include="Entity.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="f4mp.cpp" />
<ClCompile Include="NPC.cpp" />
<ClCompile Include="Player.cpp" />
<ClCompile Include="TopicInfoIDs.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Animations.h" />
<ClInclude Include="Animator.h" />
<ClInclude Include="Character.h" />
<ClInclude Include="client.h" />
<ClInclude Include="Clone.h" />
<ClInclude Include="Entity.h" />
<ClInclude Include="f4mp.h" />
<ClInclude Include="common.h" />
<ClInclude Include="NPC.h" />
<ClInclude Include="Player.h" />
<ClInclude Include="TopicInfoIDs.h" />
</ItemGroup>
<ItemGroup>
<None Include="exports.def" />
<None Include="scripts\F4MP.psc" />
<None Include="scripts\F4MPFirePoint.psc" />
<None Include="scripts\F4MPPlayer.psc" />
<None Include="scripts\F4MPQuest.psc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="소스 파일">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="헤더 파일">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="리소스 파일">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="scripts">
<UniqueIdentifier>{beb86421-8d4e-4371-821a-5300dbf76c64}</UniqueIdentifier>
</Filter>
<Filter Include="f4se">
<UniqueIdentifier>{2009725f-77b9-4ae9-9786-54d49666f207}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="..\f4se\PapyrusArgs.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\PapyrusVM.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameExtraData.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameTypes.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameAPI.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\PapyrusInterfaces.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\PapyrusValue.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameUtilities.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="f4mp.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="..\f4se\PapyrusEvents.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\NiObjects.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\NiTypes.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameData.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameFormComponents.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameForms.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameRTTI.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="Entity.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Player.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameReferences.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="NPC.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="..\f4se\PapyrusForm.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameObjects.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="common.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Character.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="..\f4se\GameEvents.cpp">
<Filter>f4se</Filter>
</ClCompile>
<ClCompile Include="TopicInfoIDs.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Animator.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Animations.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="exports.def">
<Filter>소스 파일</Filter>
</None>
<None Include="scripts\F4MP.psc">
<Filter>scripts</Filter>
</None>
<None Include="scripts\F4MPFirePoint.psc">
<Filter>scripts</Filter>
</None>
<None Include="scripts\F4MPPlayer.psc">
<Filter>scripts</Filter>
</None>
<None Include="scripts\F4MPQuest.psc">
<Filter>scripts</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="f4mp.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="common.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Clone.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="client.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Player.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Entity.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="NPC.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Character.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="TopicInfoIDs.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Animator.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Animations.h">
<Filter>헤더 파일</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommand>D:\SteamLibrary\steamapps\common\Fallout 4\f4se_loader.exe</LocalDebuggerCommand>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerCommandArguments>
</LocalDebuggerCommandArguments>
<LocalDebuggerWorkingDirectory>D:\SteamLibrary\steamapps\common\Fallout 4\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommand>D:\SteamLibrary\steamapps\common\Fallout 4\f4se_loader.exe</LocalDebuggerCommand>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerCommandArguments>
</LocalDebuggerCommandArguments>
<LocalDebuggerWorkingDirectory>D:\SteamLibrary\steamapps\common\Fallout 4\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,55 @@
#include "f4se/PluginAPI.h" // super
#include "f4se_common/f4se_version.h" // What version of SKSE is running?
#include <shlobj.h> // CSIDL_MYCODUMENTS
#include "f4mp.h"
extern "C" {
bool F4SEPlugin_Query(const F4SEInterface* f4se, PluginInfo* info) { // Called by SKSE to learn about this plugin and check that it's safe to load it
gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Fallout4\\F4MP\\F4MP.log");
gLog.SetPrintLevel(IDebugLog::kLevel_Error);
gLog.SetLogLevel(IDebugLog::kLevel_DebugMessage);
// populate info structure
info->infoVersion = PluginInfo::kInfoVersion;
info->name = "F4MP";
info->version = 1;
// store plugin handle so we can identify ourselves later
// g_pluginHandle = f4se->GetPluginHandle();
if (f4se->isEditor)
{
_MESSAGE("loaded in editor, marking as incompatible");
return false;
}
else if (f4se->runtimeVersion != RUNTIME_VERSION_1_10_163)
{
_MESSAGE("unsupported runtime version %08X", f4se->runtimeVersion);
return false;
}
// ### do not do anything else in this callback
// ### only fill out PluginInfo and return true/false
// supported runtime version
return true;
}
bool F4SEPlugin_Load(const F4SEInterface* f4se) { // Called by SKSE to load this plugin
_MESSAGE("F4MP loaded");
//Check if the function registration was a success...
bool btest = f4mp::F4MP::GetInstance().Init(f4se);
if (btest) {
_MESSAGE("Register succeeded");
}
return true;
}
};

View file

@ -0,0 +1,40 @@
Scriptname F4MP Native Hidden
int Function GetClientInstanceID() global native
Function SetClient(int instanceID) global native
bool Function IsConnected() global native
bool Function Connect(Actor player, ActorBase playerActorBase, string address, int port) global native
bool Function Disconnect() global native
Function Tick() global native
Function SyncWorld() global native
int Function GetPlayerEntityID() global native
int Function GetEntityID(ObjectReference ref) global native
Function SetEntityRef(int entityID, ObjectReference ref) global native
bool Function IsEntityValid(int entityID) global native
float[] Function GetEntityPosition(int entityID) global native
Function SetEntityPosition(int entityID, float x, float y, float z) global native
Function SetEntVarNum(int entityID, string name, float value) global native
Function SetEntVarAnim(int entityID, string animState) global native
float Function GetEntVarNum(int entityID, string name) global native
string Function GetEntVarAnim(int entityID) global native
ObjectReference[] Function GetRefsInCell(Cell cell) global native
float Function Atan2(float y, float x) global native
string Function GetWalkDir(float dX, float dY, float angleZ) global native
Action Function GetAction(string name) global native
Function Inspect(Form[] forms) global native
bool Function AnimLoops(string animState) global native
Function CopyAppearance(ActorBase src, ActorBase dest) global native
Function CopyWornItems(Actor src, Actor dest) global native
Function PlayerHit(int hitter, int hittee, float damage) global native
Function PlayerFireWeapon() global native
Function TopicInfoBegin(Form info, ObjectReference speaker) global native
ObjectReference[] Function GetEntitySyncRefs(bool clear) global native
float[] Function GetEntitySyncTransforms(bool clear) global native

View file

@ -0,0 +1,30 @@
Scriptname F4MPEntitySync extends activemagiceffect
;int updateTimer = 10
;
;int entityID = -1
;bool mine = false
;
;Event OnEffectStart(Actor akTarget, Actor akCaster)
; entityID = F4MP.GetEntityID(akTarget)
; mine = F4MP.IsEntityMine(entityID)
;
; StartTimer(0, updateTimer)
;EndEvent
;
;Event OnTimer(int timerID)
; If timerID == updateTimer
; Actor entity = GetCasterActor()
; If F4MP.IsEntityValid(entityID)
; If mine
; F4MP.SetEntityPosition(entityID, entity.x, entity.y, entity.z)
; Else
; float[] position = F4MP.GetEntityPosition(entityID)
; float angleZ = F4MP.GetEntVarNum(entityID, "angleZ")
; float distance = Math.Sqrt(Math.Pow(position[0] - entity.x, 2) + Math.Pow(position[1] - entity.y, 2) + Math.Pow(position[2] - entity.z, 2))
; entity.TranslateTo(position[0], position[1], position[2], 0.0, 0.0, angleZ, distance * 3.0, 500.0)
; EndIf
; EndIf
; StartTimer(0, updateTimer)
; EndIf
;EndEvent

View file

@ -0,0 +1,33 @@
Scriptname F4MPFirePoint extends ObjectReference
Weapon Property myWeapon = None Auto
Ammo Property myAmmo = None Auto
F4MPPlayer Property myOwner = None Auto
Event OnInit()
; SetPosition(x + 100.0, y + 100.0, z + 100.0)
SetScale(0.0)
EndEvent
Event OnLoad()
SetMotionType(Motion_Keyframed)
float[] position = F4MP.GetEntityPosition(myOwner.entityID)
float ax = F4MP.GetEntVarNum(myOwner.entityID, "angleX")
float az = F4MP.GetEntVarNum(myOwner.entityID, "angleZ")
SetPosition(position[0] + Math.Sin(az) * 100.0 + Math.Cos(az) * 10.0, position[1] + Math.Cos(az) * 100.0 - Math.Sin(az) * 10.0, position[2] + 90.0)
SetAngle(ax, 0.0, az)
; Actor player = Game.GetPlayer()
; float dx = player.x - x
; float dy = player.y - y
; SetAngle(0.0, 0.0, Math.RadiansToDegrees(F4MP.Atan2(dx, dy)))
InstanceData:Owner owner = myWeapon.GetInstanceOwner()
InstanceData.SetAttackDamage(owner, 0)
InstanceData.SetDamageTypes(owner, new InstanceData:DamageTypeInfo[0])
myWeapon.Fire(self, myAmmo)
Delete()
EndEvent

View file

@ -0,0 +1,407 @@
Scriptname F4MPPlayer extends Actor
int Property entityID = -1 Auto
Idle Property animNull Auto
Idle Property animIdle Auto
Idle Property animJogForward Auto
Idle Property animJogBackward Auto
Idle Property animJogLeft Auto
Idle Property animJogRight Auto
Idle Property animJumpUp Auto
Idle Property animJumpFall Auto
Idle Property animJumpLand Auto
Idle Property animFireWeapon Auto
Idle Property animTest Auto
Action Property actionAnim Auto
ActorValue Property avType Auto
Keyword Property animKeyword Auto
Keyword Property animType Auto
Actor Property playerRef Auto
ActorValue Property HealthAV Auto
int tickTimerID = 10
int animTimerID = 20
int tidyTimerID = 30
string animState = "INIT"
Form[] Property itemsToWear = None Auto
Weapon Property myWeapon Auto
Ammo Property myAmmo Auto
Form Property firePoint Auto
Projectile[] Property myProjectiles Auto
float Property health = 1.0 Auto
bool Property initialized = false Auto
Weapon Property weaponEquipped = None Auto
int stage = 0
Event OnInit()
StartTimer(0, tickTimerID)
StartTimer(0, animTimerID)
StartTimer(0, tidyTimerID)
RegisterForKey(113)
; self.EnableAI(false, true)
;EquipItem(myWeapon)
RegisterForHitEvent(self)
EndEvent
Event OnItemEquipped(Form akBaseObject, ObjectReference akReference)
Weapon equippedWeapon = akBaseObject as Weapon
If equippedWeapon
weaponEquipped = equippedWeapon
InstanceData:Owner owner = equippedWeapon.GetInstanceOwner()
InstanceData.SetAttackDamage(owner, 0)
InstanceData.SetDamageTypes(owner, new InstanceData:DamageTypeInfo[0])
EndIf
EndEvent
Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)
If akTarget == self && akAggressor == Game.GetPlayer()
Weapon sourceWeapon = akSource as Weapon
If sourceWeapon
InstanceData:Owner owner = sourceWeapon.GetInstanceOwner()
float damage = InstanceData.GetAttackDamage(owner)
F4MP.PlayerHit(F4MP.GetPlayerEntityID(), entityID, damage)
EndIf
EndIf
RegisterForHitEvent(self)
EndEvent
;Event OnAnimationEvent(ObjectReference akSource, string asEventName)
; Debug.Trace(akSource + " " + asEventName)
; Debug.Notification(asEventName)
;If akSource == Game.GetPlayer() && asEventName == "weaponFire"
; Debug.Trace(asEventName)
;EndIf
;EndEvent
Event OnKeyDown(int keyCode)
If keyCode == 113
; Debug.Notification(self.PlayIdle(idleAnim))
; self.SetValue(avType, 1)
; self.PushActorAway(self, 0)
; TranslateTo(x + 200.0, y, z, 0, 0, 0, 500.0)
; PlayIdleAction(F4MP.GetAction("ActionSprintStart"))
; Debug.Notification(ChangeAnimArchetype(animKeyword))
; Debug.Notification(ChangeAnimArchetype(animKeyword))
; Game.GetPlayer().PlayIdleAction(F4MP.GetAction("ActionMoveBackward"))
; playerRef .PlayIdleAction(F4MP.GetAction("ActionSprintStart"))
; TranslateTo(x + 1000.0, y, z, 0.0, 0.0, 0.0, 100.0)
; ChangeAnimArchetype()
; ChangeAnimArchetype(animType)
; PlayIdle(animJumpUp)
; ActorBase base = GetActorBase()
; F4MP.SetSex(base, base.GetSex() == 0)
; Debug.Notification(base.GetSex())
; SetOutfit(Game.GetPlayer().GetActorBase().GetOutfit())
; F4MP.CopyAppearance(Game.GetPlayer().GetActorBase(), GetActorBase())
; F4MP.CopyWornItems(Game.GetPlayer(), self)
; StartCombat(Game.GetPlayer())
; DrawWeapon()
; Weapon equippedWeapon = GetEquippedWeapon()
; If equippedWeapon != None
; equippedWeapon.Fire(self, myAmmo)
; EndIf
; Game.GetPlayer().GetEquippedWeapon().Fire(Game.GetPlayer(), myAmmo)
; Form[] args = new Form[0];
; args.Add(animIdle)
; args.Add(animJogForward)
; args.Add(animJogBackward)
; args.Add(animJogLeft)
; args.Add(animJogRight)
; args.Add(animJumpUp)
; F4MP.Inspect(args)
; F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
; newFirePoint.myWeapon = myWeapon
; newFirePoint.myAmmo = myAmmo
; newFirePoint.SetAngle(GetAngleX(), GetAngleY(), GetAngleZ())
;Debug.Notification(equippedWeaponRef)
;Weapon equippedWeapon = GetEquippedWeapon()
;Ammo equippedWeaponAmmo = equippedWeapon.GetAmmo()
;equippedWeapon.Fire(equippedWeaponRef, equippedWeaponAmmo)
;DrawWeapon()
;StartTimer(0.5, animTimerID)
;PlayIdle(animIdle)
;Utility.Wait(0.5)
;Idle animToPlay = None
;If stage == 0
; animToPlay = animJogForward
;ElseIf stage == 1
; animToPlay = animFireWeapon
;ElseIf stage == 2
; animToPlay = animJumpUp
;ElseIf stage == 3
; animToPlay = animIdle
;EndIf
;stage = (stage + 1) % 4
;If animToPlay != animIdle
; PlayIdle(animNull)
; Utility.Wait(0.3)
;EndIf
;If !IsWeaponDrawn()
; If GetEquippedWeapon() != myWeapon
; EquipItem(myWeapon)
; Utility.Wait(0.1)
; EndIf
; DrawWeapon()
; Utility.Wait(0.4)
;EndIf
;PlayIdle(animToPlay)
;TranslateTo(x + 10.0, y, z, 0, 0, 0, 0.01)
EndIf
EndEvent
Event OnTimer(int aiTimerID)
;int k = 0
;While k < myProjectiles.length
; ObjectReference ref = Game.FindClosestReferenceOfTypeFromRef(myProjectiles[k], Game.GetPlayer(), 100000.0)
; If ref != None
; float ax = ref.GetAngleX()
; float ay = ref.GetAngleY()
; float az = ref.GetAngleZ()
; If ax != 0.0 && az != 0.0
; string msg = k + ": (" + ref.x + ", " + ref.y + ", " + ref.z + "), (" + ax + ", " + ay + ", " + az + ")"
; Debug.Trace(msg)
; Debug.Notification(msg)
; F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
; newFirePoint.myWeapon = myWeapon
; newFirePoint.myAmmo = myAmmo
; newFirePoint.SetAngle(ax, ay, az - GetAngleZ())
; EndIf
; k = myProjectiles.length
; EndIf
; k += 1
;EndWhile
; PlayIdleAction(F4MP.GetAction("ActionMoveStart"))
; SetAnimationVariableFloat("Speed", 100.0)
; Debug.Trace(Game.GetPlayer().GetAnimationVariableFloat("Speed"))
; Game.GetPlayer().SetAnimationVariableFloat("Speed", 10.0)
; F4MP.SetTransforms(self)
If aiTimerID == tickTimerID
If itemsToWear != None
int i = 0
While i < itemsToWear.length
EquipItem(itemsToWear[i])
i += 1
EndWhile
itemsToWear = None
EndIf
If F4MP.IsEntityValid(entityID)
If !initialized
initialized = true
EndIf
;float[] position = F4MP.GetEntityPosition(entityID)
;float angleZ = F4MP.GetEntVarNum(entityID, "angleZ")
;float distance = Math.Sqrt(Math.Pow(position[0] - x, 2) + Math.Pow(position[1] - y, 2) + Math.Pow(position[2] - z, 2))
;health = F4MP.GetEntVarNum(entityID, "health")
;TranslateTo(position[0], position[1], position[2], 0.0, 0.0, angleZ, distance * 3.0, 500.0)
;self.SetPosition(position[0], position[1], position[2])
;self.SetAngle(0.0, 0.0, angleZ)
; Debug.Notification(entityID + " " + position[0] + " " + position[1] + " " + position[2])
Else
If initialized
;Debug.Trace("entity with ID " + entityID + " is supposed to be deleted but I saved it for ya! ;)")
Delete()
EndIf
; Debug.Notification(entityID + "!")
EndIf
StartTimer(0, tickTimerID)
ElseIf aiTimerID == animTimerID
string newAnimState = F4MP.GetEntVarAnim(entityID)
If newAnimState != animState
string oldAnimState = animState
animState = newAnimState
If !IsWeaponDrawn()
DrawWeapon()
Utility.Wait(0.4)
EndIf
If F4MP.AnimLoops(oldAnimState)
PlayIdle(animNull)
Utility.Wait(0.2)
EndIf
PlayAnimByState(newAnimState)
EndIf
StartTimer(0, animTimerID)
Elseif aiTimerID == tidyTimerID
StartTimer(0, tidyTimerID)
If GetEquippedWeapon() != weaponEquipped
EquipItem(weaponEquipped)
EndIf
If !IsWeaponDrawn()
animState = "INIT"
EndIf
;string[] animStates = new string[0]
;animStates.Add("None")
;animStates.Add("JogForward")
;animStates.Add("JogBackward")
;animStates.Add("JogLeft")
;animStates.Add("JogRight")
;animStates.Add("JumpUp")
;animStates.Add("WeaponFire")
;
;string newAnimState = animStates[Utility.RandomInt(0, animStates.length - 1)];nextAnimState;
;If newAnimState != animState
; If GetEquippedWeapon() != weaponEquipped
; EquipItem(weaponEquipped)
; EndIf
;
; If IsWeaponDrawn()
; ;If newAnimState == "None"
; ; PlayAnimByState(newAnimState)
; ;Else
; PlayIdle(animNull)
; ;StartTimer(0.3, animTimerID)
; Utility.Wait(0.3)
; PlayAnimByState(newAnimState)
; ;EndIf
; Else
; DrawWeapon()
;
; PlayIdle(animNull)
; ;StartTimer(0.4, animTimerID)
; Utility.Wait(0.4)
; PlayAnimByState(newAnimState)
; EndIf
; animState = newAnimState
;EndIf
SetValuePercentage(HealthAV, health)
If IsInCombat()
StopCombat()
EndIf
EndIf
EndEvent
Function FireWeapon()
Weapon equippedWeapon = GetEquippedWeapon()
If equippedWeapon
F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
newFirePoint.myWeapon = equippedWeapon
newFirePoint.myAmmo = equippedWeapon.GetAmmo()
newFirePoint.myOwner = self
EndIf
EndFunction
Function PlayAnimByState(string newAnimState)
Idle animToPlay = GetAnimByState(newAnimState)
If animToPlay
PlayIdle(animToPlay)
EndIf
;If newAnimState == "FireWeapon"
; Weapon equippedWeapon = GetEquippedWeapon()
; If equippedWeapon
; F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
; newFirePoint.myWeapon = equippedWeapon
; newFirePoint.myAmmo = equippedWeapon.GetAmmo()
; newFirePoint.myOwner = self
; EndIf
; animState = "INIT"
;EndIf
EndFunction
Idle Function GetAnimByState(string newAnimState)
Idle animToPlay = animIdle
If newAnimState == "None"
animToPlay = animIdle
ElseIf newAnimState == "JogForward"
animToPlay = animJogForward
ElseIf newAnimState == "JogBackward"
animToPlay = animJogBackward
ElseIf newAnimState == "JogLeft"
animToPlay = animJogLeft
ElseIf newAnimState == "JogRight"
animToPlay = animJogRight
ElseIf newAnimState == "JumpUp"
; animToPlay = animJumpUp
ElseIf newAnimState == "JumpFall"
; animToPlay = animJumpFall
ElseIf newAnimState == "JumpLand"
; animToPlay = animJumpLand
ElseIf newAnimState == "FireWeapon"
animToPlay = animFireWeapon
EndIf
return animToPlay
EndFunction
Function SetValueActually(ActorValue type, float value)
float diff = value - GetValue(type)
If diff < 0
DamageValue(type, -diff)
Else
RestoreValue(type, diff)
EndIf
EndFunction
Function SetValuePercentage(ActorValue type, float percentage)
SetValueActually(type, percentage * GetBaseValue(type))
EndFunction

View file

@ -0,0 +1,214 @@
Scriptname F4MPQuest extends Quest
int tickTimerID = 10
int updateTimerID = 20
Actor Property playerRef Auto
ActorBase Property f4mpPlayerBase Auto
ActorValue Property healthAV Auto
Spell Property entitySyncSpell Auto
VoiceType Property malePlayerVoiceType Auto
VoiceType Property femalePlayerVoiceType Auto
int[] playerIDs
F4MPPlayer[] players
bool topicInfosRegistered = false
Event OnInit()
RegisterForKey(112)
EndEvent
Function OnEntityCreate(int entityID, Form[] itemsToWear)
Debug.Notification(entityID + " has entered the world.")
If entityID != F4MP.GetPlayerEntityID()
Actor player = Game.GetPlayer()
F4MPPlayer entity = player.PlaceActorAtMe(f4mpPlayerBase) as F4MPPlayer
F4MP.SetEntityRef(entityID, entity)
entity.entityID = entityID
If f4mpPlayerBase.GetSex() == 0
entity.SetOverrideVoiceType(malePlayerVoiceType)
Else
entity.SetOverrideVoiceType(femalePlayerVoiceType)
EndIf
entity.itemsToWear = itemsToWear
; SetWornItems(entity, itemsToWear)
playerIDs.Add(entityID)
players.Add(entity)
EndIf
EndFunction
Function OnEntityRemove(int entityID)
int index = playerIDs.Find(entityID)
If index < 0
return
EndIf
players[index].Delete()
playerIDs.Remove(index)
players.Remove(index)
EndFunction
Function OnPlayerHit(float damage)
Game.GetPlayer().DamageValue(healthAV, damage)
EndFunction
Function RegisterTopicInfos(Form[] targets)
int i = 0
While i < targets.length
RegisterForRemoteEvent(targets[i] as TopicInfo, "OnBegin")
i += 1
EndWhile
EndFunction
Function OnTopicInfoRegister(Form[] targets)
If topicInfosRegistered
return
EndIf
topicInfosRegistered = true
RegisterTopicInfos(targets)
Debug.Trace(targets.length + " topic infos have registered.")
Debug.Notification(targets.length + " topic infos have registered.")
EndFunction
Event TopicInfo.OnBegin(TopicInfo akSender, ObjectReference akSpeakerRef, bool abHasBeenSaid)
;Debug.Trace("Topic info " + akSender + " is being said by " + akSpeakerRef.GetDisplayName())
;Debug.Notification("Topic info " + akSender + " is being said by " + akSpeakerRef)
F4MP.TopicInfoBegin(akSender, akSpeakerRef)
EndEvent
;Function OnFireWeapon(int entityID)
; int index = playerIDs.Find(entityID)
; If index < 0
; return
; EndIf
;
; players[index].FireWeapon()
;EndFunction
;Function OnSpawnEntity(int formID)
; ObjectReference ref = Game.GetForm(formID) as ObjectReference
; If ref == None
; return
; EndIf
;
; Actor actorRef = ref as Actor
; If actorRef != None
; actorRef.AddSpell(entitySyncSpell)
; EndIf
;EndFunction
Function SetWornItems(Actor dest, Form[] wornItems)
int i = 0
While i < wornItems.length
Debug.Trace(i + ": " + wornItems[i])
dest.EquipItem(wornItems[i])
i += 1
EndWhile
EndFunction
; TODO: mutiple timers
bool Function Connect(string address, int port)
Actor client = Game.GetPlayer()
ActorBase clientActorBase = client.GetActorBase()
StartTimer(0, tickTimerID)
StartTimer(0, updateTimerID)
return F4MP.Connect(client, clientActorBase, address, port)
EndFunction
Sound Property mySound Auto
Topic Property myTopic Auto
Event OnKeyDown(int keyCode)
If keyCode == 112
RegisterForExternalEvent("OnTopicInfoRegister", "OnTopicInfoRegister")
RegisterForExternalEvent("OnAdditionalTopicInfoRegister", "RegisterTopicInfos")
Connect("", 7779)
playerIDs = new int[0]
players = new F4MPPlayer[0]
;Actor player = Game.GetPlayer()
;F4MPPlayer entity = player.PlaceActorAtMe(f4mpPlayerBase) as F4MPPlayer
Actor client = Game.GetPlayer()
RegisterForAnimationEvent(client, "JumpUp")
RegisterForAnimationEvent(client, "weaponFire")
; RegisterForAnimationEvent(client, "JumpFall")
; RegisterForAnimationEvent(client, "JumpDown")
; RegisterForExternalEvent("OnCopyWornItems", "OnCopyWornItems")
RegisterForExternalEvent("OnEntityCreate", "OnEntityCreate")
RegisterForExternalEvent("OnEntityRemove", "OnEntityRemove")
RegisterForExternalEvent("OnPlayerHit", "OnPlayerHit")
RegisterForKey(113)
RegisterForKey(114)
ElseIf keyCode == 113
F4MP.SetClient(1 - F4MP.GetClientInstanceID())
ElseIf keyCode == 114
mySound.Play(Game.GetPlayer())
;Debug.Notification(myTopic)
;Game.GetPlayer().Say(myTopic)
EndIf
EndEvent
Form Property targetForm Auto
Actor Property chosenActor Auto
ObjectReference Property targetRef Auto
Event OnAnimationEvent(ObjectReference akSource, string asEventName)
If !F4MP.IsConnected()
return
EndIf
int playerEntityID = F4MP.GetPlayerEntityID()
If F4MP.IsEntityValid(playerEntityID)
If asEventName == "JumpUp"
F4MP.SetEntVarAnim(playerEntityID, "JumpUp")
; ElseIf asEventName == "JumpFall"
; F4MP.SetEntVarAnim(playerEntityID, "JumpFall")
; ElseIf asEventName == "JumpDown"
; F4MP.SetEntVarAnim(playerEntityID, "JumpLand")
ElseIf asEventName == "weaponFire"
; F4MP.SetEntVarAnim(playerEntityID, "FireWeapon")
F4MP.PlayerFireWeapon()
EndIf
EndIf
EndEvent
Event OnTimer(int aiTimerID)
If aiTimerID == tickTimerID
F4MP.Tick()
StartTimer(0, tickTimerID)
ElseIf aiTimerID == updateTimerID
;; ***************************************
;If chosenActor != None
; chosenActor.PathToReference(targetRef, 1.0)
;EndIf
;
;; ***************************************
int playerEntityID = F4MP.GetPlayerEntityID()
If F4MP.IsEntityValid(playerEntityID)
F4MP.SetEntVarNum(playerEntityID, "health", playerRef.GetValuePercentage(healthAV))
EndIf
StartTimer(0, updateTimerID)
EndIf
EndEvent

View file

@ -0,0 +1,34 @@
Scriptname F4MP Native Hidden
int Function GetClientInstanceID() global native
Function SetClient(int instanceID) global native
bool Function IsConnected() global native
bool Function Connect(Actor player, ActorBase playerActorBase, string address, int port) global native
bool Function Disconnect() global native
Function Tick() global native
int Function GetPlayerEntityID() global native
int Function GetEntityID(ObjectReference ref) global native
bool Function IsEntityValid(int entityID) global native
float[] Function GetEntityPosition(int entityID) global native
Function SetEntityPosition(int entityID, float x, float y, float z) global native
Function SetEntVarNum(int entityID, string name, float value) global native
Function SetEntVarAnim(int entityID, string animState) global native
float Function GetEntVarNum(int entityID, string name) global native
string Function GetEntVarAnim(int entityID) global native
float Function Atan2(float y, float x) global native
string Function GetWalkDir(float dX, float dY, float angleZ) global native
Action Function GetAction(string name) global native
Function Inspect(Form[] forms) global native
bool Function AnimLoops(string animState) global native
Function CopyAppearance(ActorBase src, ActorBase dest) global native
Function CopyWornItems(Actor src, Actor dest) global native
Function PlayerHit(int hitter, int hittee, float damage) global native
Function PlayerFireWeapon() global native
Function SpawnEntity(ObjectReference ref, float x, float y, float z, float angleX, float angleY, float angleZ) global native

View file

@ -0,0 +1,36 @@
Scriptname F4MPFirePoint extends ObjectReference
Weapon Property myWeapon = None Auto
Ammo Property myAmmo = None Auto
F4MPPlayer Property myOwner = None Auto
Event OnInit()
; SetPosition(x + 100.0, y + 100.0, z + 100.0)
SetScale(0.0)
EndEvent
Event OnLoad()
SetMotionType(Motion_Keyframed)
float[] position = F4MP.GetEntityPosition(myOwner.entityID)
float angleZ = GetAngleZ()
SetPosition(position[0] + Math.Sin(angleZ) * 100.0 + Math.Cos(angleZ) * 10.0, position[1] + Math.Cos(angleZ) * 100.0 - Math.Sin(angleZ) * 10.0, position[2] + 90.0)
float ax = F4MP.GetEntVarNum(myOwner.entityID, "angleX")
float ay = F4MP.GetEntVarNum(myOwner.entityID, "angleY")
float az = F4MP.GetEntVarNum(myOwner.entityID, "angleZ")
SetAngle(ax, ay, az)
; Actor player = Game.GetPlayer()
; float dx = player.x - x
; float dy = player.y - y
; SetAngle(0.0, 0.0, Math.RadiansToDegrees(F4MP.Atan2(dx, dy)))
InstanceData:Owner owner = myWeapon.GetInstanceOwner()
InstanceData.SetAttackDamage(owner, 0)
InstanceData.SetDamageTypes(owner, new InstanceData:DamageTypeInfo[0])
myWeapon.Fire(self, myAmmo)
Delete()
EndEvent

View file

@ -0,0 +1,415 @@
Scriptname F4MPPlayer extends Actor
int Property entityID = -1 Auto
Idle Property animNull Auto
Idle Property animIdle Auto
Idle Property animJogForward Auto
Idle Property animJogBackward Auto
Idle Property animJogLeft Auto
Idle Property animJogRight Auto
Idle Property animJumpUp Auto
Idle Property animJumpFall Auto
Idle Property animJumpLand Auto
Idle Property animFireWeapon Auto
Idle Property animTest Auto
Action Property actionAnim Auto
ActorValue Property avType Auto
Keyword Property animKeyword Auto
Keyword Property animType Auto
Actor Property playerRef Auto
ActorValue Property HealthAV Auto
int tickTimerID = 10
int animTimerID = 20
int tidyTimerID = 30
string animState = "INIT"
Form[] Property itemsToWear = None Auto
Weapon Property myWeapon Auto
Ammo Property myAmmo Auto
Form Property firePoint Auto
Projectile[] Property myProjectiles Auto
float Property health = 1.0 Auto
bool Property initialized = false Auto
Weapon Property weaponEquipped = None Auto
int stage = 0
Event OnInit()
StartTimer(0, tickTimerID)
StartTimer(0, animTimerID)
StartTimer(0, tidyTimerID)
RegisterForKey(113)
; self.EnableAI(false, true)
;EquipItem(myWeapon)
RegisterForHitEvent(self)
EndEvent
Event OnItemEquipped(Form akBaseObject, ObjectReference akReference)
Weapon equippedWeapon = akBaseObject as Weapon
If equippedWeapon
weaponEquipped = equippedWeapon
InstanceData:Owner owner = equippedWeapon.GetInstanceOwner()
InstanceData.SetAttackDamage(owner, 0)
InstanceData.SetDamageTypes(owner, new InstanceData:DamageTypeInfo[0])
EndIf
EndEvent
Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)
If akTarget == self && akAggressor == Game.GetPlayer()
Weapon sourceWeapon = akSource as Weapon
If sourceWeapon
InstanceData:Owner owner = sourceWeapon.GetInstanceOwner()
float damage = InstanceData.GetAttackDamage(owner)
F4MP.PlayerHit(F4MP.GetPlayerEntityID(), entityID, damage)
EndIf
EndIf
RegisterForHitEvent(self)
EndEvent
;Event OnAnimationEvent(ObjectReference akSource, string asEventName)
; Debug.Trace(akSource + " " + asEventName)
; Debug.Notification(asEventName)
;If akSource == Game.GetPlayer() && asEventName == "weaponFire"
; Debug.Trace(asEventName)
;EndIf
;EndEvent
Event OnKeyDown(int keyCode)
If keyCode == 113
; Debug.Notification(self.PlayIdle(idleAnim))
; self.SetValue(avType, 1)
; self.PushActorAway(self, 0)
; TranslateTo(x + 200.0, y, z, 0, 0, 0, 500.0)
; PlayIdleAction(F4MP.GetAction("ActionSprintStart"))
; Debug.Notification(ChangeAnimArchetype(animKeyword))
; Debug.Notification(ChangeAnimArchetype(animKeyword))
; Game.GetPlayer().PlayIdleAction(F4MP.GetAction("ActionMoveBackward"))
; playerRef .PlayIdleAction(F4MP.GetAction("ActionSprintStart"))
; TranslateTo(x + 1000.0, y, z, 0.0, 0.0, 0.0, 100.0)
; ChangeAnimArchetype()
; ChangeAnimArchetype(animType)
; PlayIdle(animJumpUp)
; ActorBase base = GetActorBase()
; F4MP.SetSex(base, base.GetSex() == 0)
; Debug.Notification(base.GetSex())
; SetOutfit(Game.GetPlayer().GetActorBase().GetOutfit())
; F4MP.CopyAppearance(Game.GetPlayer().GetActorBase(), GetActorBase())
; F4MP.CopyWornItems(Game.GetPlayer(), self)
; StartCombat(Game.GetPlayer())
; DrawWeapon()
; Weapon equippedWeapon = GetEquippedWeapon()
; If equippedWeapon != None
; equippedWeapon.Fire(self, myAmmo)
; EndIf
; Game.GetPlayer().GetEquippedWeapon().Fire(Game.GetPlayer(), myAmmo)
; Form[] args = new Form[0];
; args.Add(animIdle)
; args.Add(animJogForward)
; args.Add(animJogBackward)
; args.Add(animJogLeft)
; args.Add(animJogRight)
; args.Add(animJumpUp)
; F4MP.Inspect(args)
; F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
; newFirePoint.myWeapon = myWeapon
; newFirePoint.myAmmo = myAmmo
; newFirePoint.SetAngle(GetAngleX(), GetAngleY(), GetAngleZ())
;Debug.Notification(equippedWeaponRef)
;Weapon equippedWeapon = GetEquippedWeapon()
;Ammo equippedWeaponAmmo = equippedWeapon.GetAmmo()
;equippedWeapon.Fire(equippedWeaponRef, equippedWeaponAmmo)
;DrawWeapon()
;StartTimer(0.5, animTimerID)
;PlayIdle(animIdle)
;Utility.Wait(0.5)
;Idle animToPlay = None
;If stage == 0
; animToPlay = animJogForward
;ElseIf stage == 1
; animToPlay = animFireWeapon
;ElseIf stage == 2
; animToPlay = animJumpUp
;ElseIf stage == 3
; animToPlay = animIdle
;EndIf
;stage = (stage + 1) % 4
;If animToPlay != animIdle
; PlayIdle(animNull)
; Utility.Wait(0.3)
;EndIf
;If !IsWeaponDrawn()
; If GetEquippedWeapon() != myWeapon
; EquipItem(myWeapon)
; Utility.Wait(0.1)
; EndIf
; DrawWeapon()
; Utility.Wait(0.4)
;EndIf
;PlayIdle(animToPlay)
;TranslateTo(x + 10.0, y, z, 0, 0, 0, 0.01)
EndIf
EndEvent
Event OnTimer(int aiTimerID)
;int k = 0
;While k < myProjectiles.length
; ObjectReference ref = Game.FindClosestReferenceOfTypeFromRef(myProjectiles[k], Game.GetPlayer(), 100000.0)
; If ref != None
; float ax = ref.GetAngleX()
; float ay = ref.GetAngleY()
; float az = ref.GetAngleZ()
; If ax != 0.0 && az != 0.0
; string msg = k + ": (" + ref.x + ", " + ref.y + ", " + ref.z + "), (" + ax + ", " + ay + ", " + az + ")"
; Debug.Trace(msg)
; Debug.Notification(msg)
; F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
; newFirePoint.myWeapon = myWeapon
; newFirePoint.myAmmo = myAmmo
; newFirePoint.SetAngle(ax, ay, az - GetAngleZ())
; EndIf
; k = myProjectiles.length
; EndIf
; k += 1
;EndWhile
; PlayIdleAction(F4MP.GetAction("ActionMoveStart"))
; SetAnimationVariableFloat("Speed", 100.0)
; Debug.Trace(Game.GetPlayer().GetAnimationVariableFloat("Speed"))
; Game.GetPlayer().SetAnimationVariableFloat("Speed", 10.0)
; F4MP.SetTransforms(self)
If aiTimerID == tickTimerID
If itemsToWear != None
int i = 0
While i < itemsToWear.length
Debug.Trace(itemsToWear[i].GetName())
i += 1
EndWhile
i = 0
While i < itemsToWear.length
EquipItem(itemsToWear[i])
i += 1
EndWhile
itemsToWear = None
EndIf
If F4MP.IsEntityValid(entityID)
If !initialized
initialized = true
EndIf
float[] position = F4MP.GetEntityPosition(entityID)
float angleX = F4MP.GetEntVarNum(entityID, "angleX")
float angleY = F4MP.GetEntVarNum(entityID, "angleY")
float angleZ = F4MP.GetEntVarNum(entityID, "angleZ")
float distance = Math.Sqrt(Math.Pow(position[0] - x, 2) + Math.Pow(position[1] - y, 2) + Math.Pow(position[2] - z, 2))
;health = F4MP.GetEntVarNum(entityID, "health")
TranslateTo(position[0], position[1], position[2], 0.0, 0.0, angleZ, distance * 3.0, 200.0)
;self.SetPosition(position[0], position[1], position[2])
;self.SetAngle(0.0, 0.0, angleZ)
; Debug.Notification(entityID + " " + position[0] + " " + position[1] + " " + position[2])
Else
If initialized
;Debug.Trace("entity with ID " + entityID + " is supposed to be deleted but I saved it for ya! ;)")
Delete()
EndIf
; Debug.Notification(entityID + "!")
EndIf
StartTimer(0, tickTimerID)
ElseIf aiTimerID == animTimerID
string newAnimState = F4MP.GetEntVarAnim(entityID)
If newAnimState != animState
string oldAnimState = animState
animState = newAnimState
If !IsWeaponDrawn()
DrawWeapon()
Utility.Wait(0.4)
EndIf
If F4MP.AnimLoops(oldAnimState)
PlayIdle(animNull)
Utility.Wait(0.2)
EndIf
PlayAnimByState(newAnimState)
EndIf
StartTimer(0, animTimerID)
Elseif aiTimerID == tidyTimerID
StartTimer(0, tidyTimerID)
If GetEquippedWeapon() != weaponEquipped
EquipItem(weaponEquipped)
EndIf
If !IsWeaponDrawn()
animState = "INIT"
EndIf
;string[] animStates = new string[0]
;animStates.Add("None")
;animStates.Add("JogForward")
;animStates.Add("JogBackward")
;animStates.Add("JogLeft")
;animStates.Add("JogRight")
;animStates.Add("JumpUp")
;animStates.Add("WeaponFire")
;
;string newAnimState = animStates[Utility.RandomInt(0, animStates.length - 1)];nextAnimState;
;If newAnimState != animState
; If GetEquippedWeapon() != weaponEquipped
; EquipItem(weaponEquipped)
; EndIf
;
; If IsWeaponDrawn()
; ;If newAnimState == "None"
; ; PlayAnimByState(newAnimState)
; ;Else
; PlayIdle(animNull)
; ;StartTimer(0.3, animTimerID)
; Utility.Wait(0.3)
; PlayAnimByState(newAnimState)
; ;EndIf
; Else
; DrawWeapon()
;
; PlayIdle(animNull)
; ;StartTimer(0.4, animTimerID)
; Utility.Wait(0.4)
; PlayAnimByState(newAnimState)
; EndIf
; animState = newAnimState
;EndIf
SetValuePercentage(HealthAV, health)
If IsInCombat()
StopCombat()
EndIf
EndIf
EndEvent
Function FireWeapon()
Weapon equippedWeapon = GetEquippedWeapon()
If equippedWeapon
F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
newFirePoint.myWeapon = equippedWeapon
newFirePoint.myAmmo = equippedWeapon.GetAmmo()
newFirePoint.myOwner = self
EndIf
EndFunction
Function PlayAnimByState(string newAnimState)
Idle animToPlay = GetAnimByState(newAnimState)
If animToPlay
PlayIdle(animToPlay)
EndIf
;If newAnimState == "FireWeapon"
; Weapon equippedWeapon = GetEquippedWeapon()
; If equippedWeapon
; F4MPFirePoint newFirePoint = PlaceAtMe(firePoint) as F4MPFirePoint
; newFirePoint.myWeapon = equippedWeapon
; newFirePoint.myAmmo = equippedWeapon.GetAmmo()
; newFirePoint.myOwner = self
; EndIf
; animState = "INIT"
;EndIf
EndFunction
Idle Function GetAnimByState(string newAnimState)
Idle animToPlay = animIdle
If newAnimState == "None"
animToPlay = animIdle
ElseIf newAnimState == "JogForward"
animToPlay = animJogForward
ElseIf newAnimState == "JogBackward"
animToPlay = animJogBackward
ElseIf newAnimState == "JogLeft"
animToPlay = animJogLeft
ElseIf newAnimState == "JogRight"
animToPlay = animJogRight
ElseIf newAnimState == "JumpUp"
; animToPlay = animJumpUp
ElseIf newAnimState == "JumpFall"
; animToPlay = animJumpFall
ElseIf newAnimState == "JumpLand"
; animToPlay = animJumpLand
ElseIf newAnimState == "FireWeapon"
animToPlay = animFireWeapon
EndIf
return animToPlay
EndFunction
Function SetValueActually(ActorValue type, float value)
float diff = value - GetValue(type)
If diff < 0
DamageValue(type, -diff)
Else
RestoreValue(type, diff)
EndIf
EndFunction
Function SetValuePercentage(ActorValue type, float percentage)
SetValueActually(type, percentage * GetBaseValue(type))
EndFunction

View file

@ -0,0 +1,248 @@
Scriptname F4MPQuest extends Quest
int tickTimerID = 10
int queryTimerID = 20
ActorBase Property f4mpPlayerBase Auto
ActorValue Property healthAV Auto
int[] playerIDs
F4MPPlayer[] players
Actor[] myNPCs
Actor[] othersNPCs
Event OnInit()
RegisterForKey(112)
EndEvent
Function OnEntityCreate(int entityID, Form[] itemsToWear)
Debug.Notification(entityID + " has entered the world.")
If entityID != F4MP.GetPlayerEntityID()
Actor player = Game.GetPlayer()
F4MPPlayer entity = player.PlaceActorAtMe(f4mpPlayerBase) as F4MPPlayer
entity.entityID = entityID
entity.itemsToWear = itemsToWear
; SetWornItems(entity, itemsToWear)
playerIDs.Add(entityID)
players.Add(entity)
EndIf
EndFunction
Function OnEntityUpdate(int entityID)
; int index = playerIDs.Find(entityID)
; If index < 0
; return
; EndIf
; F4MPPlayer player = players[index];
; If F4MP.IsEntityValid(player.entityID)
; float[] position = F4MP.GetEntityPosition(player.entityID)
; float angleX = 0.0; F4MP.ReadNumber()
; float angleY = 0.0; F4MP.ReadNumber()
; float angleZ = 0.0; F4MP.ReadNumber()
; Debug.Trace(position[0] + " " + position[1] + " " + position[2] + " " + angleX + " " + angleY + " " + angleZ)
; player.TranslateTo(position[0], position[1], position[2], angleX, angleY, angleZ, 200.0, 100.0)
; EndIf
EndFunction
Function OnEntityRemove(int entityID)
int index = playerIDs.Find(entityID)
If index < 0
return
EndIf
players[index].Delete()
playerIDs.Remove(index)
players.Remove(index)
EndFunction
Function OnClientUpdate(int entityID)
; Actor player = Game.GetPlayer()
; int playerEntityID = F4MP.GetPlayerEntityID()
; If F4MP.IsEntityValid(playerEntityID)
; F4MP.SetEntityPosition(playerEntityID, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ())
; F4MP.WriteNumber(player.GetAngleX())
; F4MP.WriteNumber(player.GetAngleY())
; F4MP.WriteNumber(player.GetAngleZ())
; EndIf
EndFunction
Function OnPlayerHit(float damage)
Game.GetPlayer().DamageValue(healthAV, damage)
EndFunction
Function OnFireWeapon(int entityID)
int index = playerIDs.Find(entityID)
If index < 0
return
EndIf
players[index].FireWeapon()
EndFunction
Function OnSpawnEntity(int formID)
ObjectReference ref = Game.GetForm(formID) as ObjectReference
If ref == None
return
EndIf
Actor actorRef = ref as Actor
If actorRef != None && myNPCs.Find(actorRef) < 0
othersNPCs.Add(actorRef)
EndIf
EndFunction
Function SetWornItems(Actor dest, Form[] wornItems)
int i = 0
While i < wornItems.length
Debug.Trace(i + ": " + wornItems[i])
dest.EquipItem(wornItems[i])
i += 1
EndWhile
EndFunction
; TODO: mutiple timers
bool Function Connect(string address, int port)
Actor client = Game.GetPlayer()
ActorBase clientActorBase = client.GetActorBase()
StartTimer(0, tickTimerID)
return F4MP.Connect(client, clientActorBase, address, port)
EndFunction
Event OnKeyDown(int keyCode)
If keyCode == 112
Connect("", 7779)
playerIDs = new int[0]
players = new F4MPPlayer[0]
othersNPCs = new Actor[0]
myNPCs = new Actor[0]
StartTimer(0, queryTimerID)
;Actor player = Game.GetPlayer()
;F4MPPlayer entity = player.PlaceActorAtMe(f4mpPlayerBase) as F4MPPlayer
Actor client = Game.GetPlayer()
RegisterForAnimationEvent(client, "JumpUp")
RegisterForAnimationEvent(client, "weaponFire")
; RegisterForAnimationEvent(client, "JumpFall")
; RegisterForAnimationEvent(client, "JumpDown")
; RegisterForExternalEvent("OnCopyWornItems", "OnCopyWornItems")
RegisterForExternalEvent("OnEntityCreate", "OnEntityCreate")
RegisterForExternalEvent("OnEntityUpdate", "OnEntityUpdate")
RegisterForExternalEvent("OnEntityRemove", "OnEntityRemove")
RegisterForExternalEvent("OnClientUpdate", "OnClientUpdate")
RegisterForExternalEvent("OnPlayerHit", "OnPlayerHit")
RegisterForExternalEvent("OnFireWeapon", "OnFireWeapon")
RegisterForExternalEvent("OnSpawnEntity", "OnSpawnEntity")
RegisterForKey(113)
RegisterForKey(114)
ElseIf keyCode == 113
F4MP.SetClient(1 - F4MP.GetClientInstanceID())
ElseIf keyCode == 114
; Actor randomActor = Game.FindRandomActorFromRef(Game.GetPlayer(), 1000.0)
; If randomActor != None && randomActor != Game.GetPlayer()
; chosenActor = randomActor
; targetRef = Game.GetPlayer().PlaceAtMe(targetForm)
; Debug.Notification(chosenActor.GetDisplayName() + " " + targetRef.GetDisplayName())
; EndIf
EndIf
EndEvent
Form Property targetForm Auto
Actor Property chosenActor Auto
ObjectReference Property targetRef Auto
Event OnAnimationEvent(ObjectReference akSource, string asEventName)
If !F4MP.IsConnected()
return
EndIf
int playerEntityID = F4MP.GetPlayerEntityID()
If F4MP.IsEntityValid(playerEntityID)
If asEventName == "JumpUp"
F4MP.SetEntVarAnim(playerEntityID, "JumpUp")
; ElseIf asEventName == "JumpFall"
; F4MP.SetEntVarAnim(playerEntityID, "JumpFall")
; ElseIf asEventName == "JumpDown"
; F4MP.SetEntVarAnim(playerEntityID, "JumpLand")
ElseIf asEventName == "weaponFire"
; F4MP.SetEntVarAnim(playerEntityID, "FireWeapon")
F4MP.PlayerFireWeapon()
EndIf
EndIf
EndEvent
Event OnTimer(int aiTimerID)
If !F4MP.IsConnected()
F4MP.Tick()
StartTimer(0, aiTimerID)
return
EndIf
If aiTimerID == tickTimerID
StartTimer(0, tickTimerID)
;; ***************************************
;If chosenActor != None
; chosenActor.PathToReference(targetRef, 1.0)
;EndIf
;
;; ***************************************
Actor player = Game.GetPlayer()
int playerEntityID = F4MP.GetPlayerEntityID()
If F4MP.IsEntityValid(playerEntityID)
F4MP.SetEntityPosition(playerEntityID, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ())
F4MP.SetEntVarNum(playerEntityID, "angleX", player.GetAngleX())
F4MP.SetEntVarNum(playerEntityID, "angleY", player.GetAngleY())
F4MP.SetEntVarNum(playerEntityID, "angleZ", player.GetAngleZ())
F4MP.SetEntVarNum(playerEntityID, "health", player.GetValuePercentage(healthAV))
EndIf
;int i = 0
;While i < myNPCs.length
; Actor myNPC = myNPCs[i]
; F4MP.SetEntityPosition(F4MP.GetEntityID(myNPC), myNPC.x, myNPC.y, myNPC.z)
; i += 1
;EndWhile
F4MP.Tick()
;i = 0
;While i < othersNPCs.length
; Actor othersNPC = othersNPCs[i]
; float[] position = F4MP.GetEntityPosition(F4MP.GetEntityID(othersNPC))
; float distance = Math.Sqrt(Math.Pow(position[0] - othersNPC.x, 2) + Math.Pow(position[1] - othersNPC.y, 2) + Math.Pow(position[2] - othersNPC.z, 2))
; othersNPC.TranslateTo(position[0], position[1], position[2], 0.0, 0.0, othersNPC.GetAngleZ(), distance * 3.0, 200.0)
; i += 1
;EndWhile
; Debug.Notification(F4MP.GetPlayerEntityID() + " " + player.GetPositionX() + " " + player.GetPositionY() + " " + player.GetPositionZ())
ElseIf aiTimerID == queryTimerID
StartTimer(0, queryTimerID)
;Actor randomActor = Game.FindRandomActorFromRef(Game.GetPlayer(), 10000.0)
;If randomActor != None && randomActor as F4MPPlayer == None && randomActor != Game.GetPlayer()
; If myNPCs.Find(randomActor) < 0 && othersNPCs.Find(randomActor) < 0
; myNPCs.Add(randomActor)
; F4MP.SpawnEntity(randomActor, randomActor.x, randomActor.y, randomActor.z, randomActor.GetAngleX(), randomActor.GetAngleY(), randomActor.GetAngleZ())
; EndIf
;EndIf
EndIf
EndEvent

View file

@ -0,0 +1,22 @@
#include "Character.h"
f4mp::Character::Character()
{
}
void f4mp::Character::OnEntityUpdate(librg_event* event)
{
Entity::OnEntityUpdate(event);
Utils::Write(event->data, nodeTransforms);
Utils::Write(event->data, transformTime);
}
void f4mp::Character::OnClientUpdate(librg_event* event)
{
Entity::OnClientUpdate(event);
Utils::Read(event->data, nodeTransforms);
Utils::Read(event->data, transformTime);
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "Entity.h"
namespace f4mp
{
class Character : public Entity
{
public:
Character();
void OnEntityUpdate(librg_event* event) override;
void OnClientUpdate(librg_event* event) override;
private:
std::vector<float> nodeTransforms;
double transformTime;
};
}

View file

@ -0,0 +1,106 @@
#include "Entity.h"
#include "Player.h"
#include "NPC.h"
f4mp::Entity* f4mp::Entity::Get(librg_entity* entity)
{
if (entity == nullptr)
{
return nullptr;
}
return (Entity*)entity->user_data;
}
f4mp::Entity* f4mp::Entity::Get(librg_event* event)
{
return Get(event->entity);
}
f4mp::Entity* f4mp::Entity::Get(librg_peer* peer)
{
return (Entity*)peer->data;
}
f4mp::Entity* f4mp::Entity::Create(librg_entity* entity)
{
Entity* instance = nullptr;
switch (entity->type)
{
case EntityType::Player:
instance = new Player();
break;
case EntityType::NPC:
instance = new NPC();
break;
}
return Create(entity, instance);
}
f4mp::Entity* f4mp::Entity::Create(librg_event* event)
{
return Create(event->entity);
}
f4mp::Entity::Entity() : entityID((u32)-1)
{
}
f4mp::Entity::~Entity()
{
}
void f4mp::Entity::OnConnectRequest(librg_event* event)
{
}
void f4mp::Entity::OnConnectAccept(librg_event* event)
{
}
void f4mp::Entity::OnConnectRefuse(librg_event* event)
{
}
void f4mp::Entity::OnDisonnect(librg_event* event)
{
delete this;
event->entity->user_data = nullptr;
}
void f4mp::Entity::OnEntityCreate(librg_event* event)
{
}
void f4mp::Entity::OnEntityUpdate(librg_event* event)
{
librg_data_wf32(event->data, angles[0]);
librg_data_wf32(event->data, angles[1]);
librg_data_wf32(event->data, angles[2]);
}
void f4mp::Entity::OnEntityRemove(librg_event* event)
{
}
void f4mp::Entity::OnClientUpdate(librg_event* event)
{
angles[0] = librg_data_rf32(event->data);
angles[1] = librg_data_rf32(event->data);
angles[2] = librg_data_rf32(event->data);
}
u32 f4mp::Entity::GetEntityID() const
{
return entityID;
}
void f4mp::Entity::SetEntityID(u32 id)
{
entityID = id;
}

View file

@ -0,0 +1,58 @@
#pragma once
#include "server_common.h"
namespace f4mp
{
class Entity
{
public:
static Entity* Get(librg_entity* entity);
static Entity* Get(librg_event* event);
static Entity* Get(librg_peer* peer);
template<class T, class U> static T* GetAs(U entity);
static Entity* Create(librg_entity* entity);
static Entity* Create(librg_event* event);
template<class T>
static T* Create(librg_entity* entity, T* instance);
Entity();
virtual ~Entity();
virtual void OnConnectRequest(librg_event* event);
virtual void OnConnectAccept(librg_event* event);
virtual void OnConnectRefuse(librg_event* event);
virtual void OnDisonnect(librg_event* event);
virtual void OnEntityCreate(librg_event* event);
virtual void OnEntityUpdate(librg_event* event);
virtual void OnEntityRemove(librg_event* event);
virtual void OnClientUpdate(librg_event* event);
u32 GetEntityID() const;
void SetEntityID(u32 id);
private:
u32 entityID;
float angles[3];
};
template<class T, class U>
T* Entity::GetAs(U entity)
{
return dynamic_cast<T*>(Get(entity));
}
template<class T>
inline T* Entity::Create(librg_entity* entity, T* instance)
{
instance->SetEntityID(entity->id);
entity->user_data = instance;
return instance;
}
}

View file

@ -0,0 +1,13 @@
#include "NPC.h"
f4mp::NPC::NPC(u32 formID, u32 ownerEntityID) : formID(formID), ownerEntityID(ownerEntityID)
{
}
void f4mp::NPC::OnEntityCreate(librg_event* event)
{
Character::OnEntityCreate(event);
librg_data_wi32(event->data, formID);
librg_data_wi32(event->data, ownerEntityID);
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "Character.h"
namespace f4mp
{
class NPC : public Character
{
public:
NPC(u32 formID = 0, u32 ownerEntityID = (u32)-1);
void OnEntityCreate(librg_event* event) override;
private:
u32 formID;
u32 ownerEntityID;
};
}

View file

@ -0,0 +1,81 @@
#include "Player.h"
const f4mp::AppearanceData& f4mp::Player::GetAppearance() const
{
return appearance;
}
const f4mp::WornItemsData& f4mp::Player::GetWornItems() const
{
return wornItems;
}
void f4mp::Player::OnConnectRequest(librg_event* event)
{
Character::OnConnectRequest(event);
AppearanceData& playerAppearance = appearance;
WornItemsData& playerWornItems = wornItems;
Utils::Read(event->data, playerAppearance.female);
Utils::Read(event->data, playerAppearance.weights);
Utils::Read(event->data, playerAppearance.hairColor);
Utils::Read(event->data, playerAppearance.headParts);
Utils::Read(event->data, playerAppearance.morphSetValue);
Utils::Read(event->data, playerAppearance.morphRegionData1);
Utils::Read(event->data, playerAppearance.morphRegionData2);
Utils::Read(event->data, playerAppearance.morphSetData1);
Utils::Read(event->data, playerAppearance.morphSetData2);
Utils::Read(event->data, playerWornItems.data1);
Utils::Read(event->data, playerWornItems.data2);
}
void f4mp::Player::OnConnectRefuse(librg_event* event)
{
Character::OnConnectRefuse(event);
delete this;
event->user_data = nullptr;
}
void f4mp::Player::OnEntityCreate(librg_event* event)
{
Character::OnEntityCreate(event);
AppearanceData& playerAppearance = appearance;
WornItemsData& playerWornItems = wornItems;
Utils::Write(event->data, playerAppearance.female);
Utils::Write(event->data, playerAppearance.weights);
Utils::Write(event->data, playerAppearance.hairColor);
Utils::Write(event->data, playerAppearance.headParts);
Utils::Write(event->data, playerAppearance.morphSetValue);
Utils::Write(event->data, playerAppearance.morphRegionData1);
Utils::Write(event->data, playerAppearance.morphRegionData2);
Utils::Write(event->data, playerAppearance.morphSetData1);
Utils::Write(event->data, playerAppearance.morphSetData2);
Utils::Write(event->data, playerWornItems.data1);
Utils::Write(event->data, playerWornItems.data2);
}
void f4mp::Player::OnEntityUpdate(librg_event* event)
{
Character::OnEntityUpdate(event);
librg_data_wf32(event->data, health);
librg_data_wi32(event->data, animState);
}
void f4mp::Player::OnClientUpdate(librg_event* event)
{
Character::OnClientUpdate(event);
health = librg_data_rf32(event->data);
animState = librg_data_ri32(event->data);
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "Character.h"
namespace f4mp
{
class Player : public Character
{
public:
void OnConnectRequest(librg_event* event) override;
void OnConnectRefuse(librg_event* event) override;
void OnEntityCreate(librg_event* event) override;
void OnEntityUpdate(librg_event* event) override;
void OnClientUpdate(librg_event* event) override;
const AppearanceData& GetAppearance() const;
const WornItemsData& GetWornItems() const;
private:
float health;
int animState;
AppearanceData appearance;
WornItemsData wornItems;
};
}

View file

@ -0,0 +1,330 @@
#pragma once
#include <librg.h>
#include "server_common.h"
#include "Entity.h"
#include "Player.h"
#include "NPC.h"
#include <cfloat>
#include <unordered_map>
namespace f4mp
{
class Server
{
private:
static Server* instance;
std::string address;
i32 port;
librg_ctx ctx;
std::unordered_map<u32, u32> entityIDs;
std::unordered_map<u32, f64> entitiesSyncedTime;
static void on_connect_request(librg_event* event)
{
Player* player = new Player{};
event->user_data = player;
event->peer->data = player;
player->OnConnectRequest(event);
librg_log("connection requested\n");
}
static void on_connect_accepted(librg_event* event)
{
librg_log("on_connect_accepted %x\n", event->peer);
librg_entity* blob = event->entity;
blob->position = zpl_vec3{ 886.134460f, -426.953460f, -1550.012817f };
librg_log("spawning player %u at: %f %f %f\n",
event->entity->id,
blob->position.x,
blob->position.y,
blob->position.z
);
Server* self = (Server*)event->ctx->user_data;
enet_peer_timeout(event->peer, UINT32_MAX, UINT32_MAX, UINT32_MAX);
librg_entity_control_set(event->ctx, event->entity->id, event->peer);
librg_entity_visibility_set(event->ctx, event->entity->id, LIBRG_ALWAYS_VISIBLE);
//librg_entity_world_set(event->ctx, event->entity->id, 0);
event->entity->user_data = event->user_data;
Entity* entity = Entity::Get(event);
if (!entity)
{
return;
}
entity->SetEntityID(event->entity->id);
entity->OnConnectAccept(event);
}
static void on_connect_refused(librg_event* event)
{
librg_log("on_connect_refused\n");
Player* player = (Player*)event->user_data;
if (!player)
{
return;
}
player->OnConnectRefuse(event);
}
static void on_connect_disconnect(librg_event* event)
{
librg_log("entity %d peer disconnected: %x\n", event->entity->id, event->peer);
Entity* entity = Entity::Get(event);
if (!entity)
{
return;
}
entity->OnDisonnect(event);
}
static void on_entity_create(librg_event* event)
{
Entity* entity = Entity::Get(event);
if (!entity)
{
return;
}
entity->OnEntityCreate(event);
}
static void on_entity_update(librg_event* event)
{
Entity* entity = Entity::Get(event);
if (!entity)
{
return;
}
entity->OnEntityUpdate(event);
}
static void on_entity_remove(librg_event* event)
{
librg_log("remove ent %d for client %x\n", event->entity->id, event->peer);
Entity* entity = Entity::Get(event);
if (!entity)
{
return;
}
entity->OnEntityRemove(event);
}
static void on_update(librg_event* event)
{
Entity* entity = Entity::Get(event);
if (!entity)
{
return;
}
entity->OnClientUpdate(event);
}
static void OnHit(librg_message* msg)
{
HitData data;
librg_data_rptr(msg->data, &data, sizeof(HitData));
librg_message_send_to(msg->ctx, MessageType::Hit, librg_entity_control_get(msg->ctx, data.hittee), &data, sizeof(HitData));
}
static void OnFireWeapon(librg_message* msg)
{
u32 entity;
librg_data_rptr(msg->data, &entity, sizeof(u32));
librg_message_send_except(msg->ctx, MessageType::FireWeapon, msg->peer, &entity, sizeof(u32));
}
static void OnSpawnEntity(librg_message* msg)
{
SpawnEntityData data;
librg_data_rptr(msg->data, &data, sizeof(SpawnEntityData));
auto entityID = instance->entityIDs.find(data.formID);
if (entityID != instance->entityIDs.end())
{
return;
}
librg_log("entity spawned: %x (%f, %f, %f) (%f, %f, %f)\n", data.formID, data.position.x, data.position.y, data.position.z, data.angles.x, data.angles.y, data.angles.z);
// TODO: change it so only the host can add entities?
librg_entity* entity = librg_entity_create(msg->ctx, EntityType::NPC);
librg_entity_control_set(msg->ctx, entity->id, msg->peer);
librg_entity_visibility_set(msg->ctx, entity->id, LIBRG_ALWAYS_VISIBLE);
entity->position = data.position;
NPC* npc = Entity::Create(entity, new NPC(data.formID, data.ownerEntityID));
data.entityID = entity->id;
data.ownerEntityID = Entity::GetAs<Player>(msg->peer)->GetEntityID();
instance->entityIDs[data.formID] = data.entityID;
//librg_message_send_all(msg->ctx, MessageType::SpawnEntity, &data, sizeof(SpawnEntityData));
}
static void OnSyncEntity(librg_message* msg)
{
SyncEntityData data;
librg_data_rptr(msg->data, &data, sizeof(SyncEntityData));
auto syncedTime = instance->entitiesSyncedTime.find(data.formID);
if (syncedTime != instance->entitiesSyncedTime.end())
{
if (data.syncedTime < syncedTime->second)
{
return;
}
}
u32 callerID = Entity::Get(msg->peer)->GetEntityID();
float minDist = zpl_vec3_mag2(data.position - librg_entity_fetch(msg->ctx, callerID)->position);
librg_entity_iteratex(msg->ctx, LIBRG_ENTITY_ALIVE | LIBRG_ENTITY_CLIENT, id,
{
if (id == callerID)
{
continue;
}
librg_entity* entity = librg_entity_fetch(msg->ctx, id);
if (zpl_vec3_mag2(data.position - entity->position) < minDist)
{
return;
}
});
instance->entitiesSyncedTime[data.formID] = data.syncedTime;
librg_message_send_except(msg->ctx, MessageType::SyncEntity, msg->peer, &data, sizeof(SyncEntityData));
}
static void OnSpawnBuilding(librg_message* msg)
{
SpawnBuildingData data;
librg_data_rptr(msg->data, &data, sizeof(SpawnBuildingData));
librg_log("building spawned: %llx %f %f %f\n", GetUniqueFormID(data.ownerEntityID, data.formID), data.position.x, data.position.y, data.position.z);
librg_message_send_except(msg->ctx, MessageType::SpawnBuilding, msg->peer, &data, sizeof(SpawnBuildingData));
}
static void OnRemoveBuilding(librg_message* msg)
{
RemoveBuildingData data;
librg_data_rptr(msg->data, &data, sizeof(RemoveBuildingData));
librg_log("building removed: %llx\n", data.uniqueFormID);
librg_message_send_except(msg->ctx, MessageType::RemoveBuilding, msg->peer, &data, sizeof(RemoveBuildingData));
}
static void OnSpeak(librg_message* msg)
{
SpeakData data;
librg_data_rptr(msg->data, &data, sizeof(SpeakData));
printf("%u: %X spoke %X\n", data.clientEntityID, data.speakerFormID, data.topicInfoFormID);
librg_message_send_except(msg->ctx, MessageType::Speak, msg->peer, &data, sizeof(SpeakData));
}
public:
Server(const std::string& address, i32 port) : address(address), port(port), ctx{}
{
instance = this;
librg_option_set(LIBRG_MAX_ENTITIES_PER_BRANCH, 4);
ctx.tick_delay = 10.0;
ctx.mode = LIBRG_MODE_SERVER;
ctx.world_size = zpl_vec3f(FLT_MAX, FLT_MAX, FLT_MAX);
ctx.min_branch_size = zpl_vec3f(-1, -1, -1);
ctx.max_entities = 60000;
ctx.max_connections = 1200;
ctx.user_data = this;
librg_init(&ctx);
librg_event_add(&ctx, LIBRG_CONNECTION_REQUEST, on_connect_request);
librg_event_add(&ctx, LIBRG_CONNECTION_ACCEPT, on_connect_accepted);
librg_event_add(&ctx, LIBRG_CONNECTION_REFUSE, on_connect_refused);
librg_event_add(&ctx, LIBRG_CONNECTION_DISCONNECT, on_connect_disconnect);
librg_event_add(&ctx, LIBRG_CLIENT_STREAMER_UPDATE, on_update);
librg_event_add(&ctx, LIBRG_ENTITY_CREATE, on_entity_create);
librg_event_add(&ctx, LIBRG_ENTITY_UPDATE, on_entity_update);
librg_event_add(&ctx, LIBRG_ENTITY_REMOVE, on_entity_remove);
librg_network_add(&ctx, MessageType::Hit, OnHit);
librg_network_add(&ctx, MessageType::FireWeapon, OnFireWeapon);
librg_network_add(&ctx, MessageType::SpawnEntity, OnSpawnEntity);
librg_network_add(&ctx, MessageType::SyncEntity, OnSyncEntity);
librg_network_add(&ctx, MessageType::SpawnBuilding, OnSpawnBuilding);
librg_network_add(&ctx, MessageType::RemoveBuilding, OnRemoveBuilding);
librg_network_add(&ctx, MessageType::Speak, OnSpeak);
librg_log("\
F4MP Copyright (C) 2020 Hyunsung Go\n\
This program comes with ABSOLUTELY NO WARRANTY.\n\
This is free software, and you are welcome to redistribute it\n\
under certain conditions; Read LICENSE.txt for full details.\n\n");
}
virtual ~Server()
{
librg_network_stop(&ctx);
librg_free(&ctx);
// just in case..
if (instance == this)
{
instance = nullptr;
}
}
void Start()
{
librg_network_start(&ctx, librg_address{ port, const_cast<char*>(address.c_str()) });
librg_log(" Server started. Listening on %s:%d\n\n", address.c_str(), port);
}
void Tick()
{
librg_tick(&ctx);
zpl_sleep_ms(1);
}
librg_ctx* GetContext()
{
return &ctx;
}
};
}

View file

@ -0,0 +1,63 @@
#include "api.h"
#define GEN_DATA_READWRITE(TYPE) \
EXPORT TYPE ZPL_JOIN2(data_r,TYPE)(struct librg_data *data){ return ZPL_JOIN2(librg_data_r,TYPE)(data); } \
EXPORT void ZPL_JOIN2(data_w,TYPE)(struct librg_data *data, TYPE value){ ZPL_JOIN2(librg_data_w,TYPE)(data, value); } \
EXPORT TYPE ZPL_JOIN3(data_r,TYPE,_at)(struct librg_data *data, isize position){ return ZPL_JOIN3(librg_data_r,TYPE,_at)(data, position); } \
EXPORT void ZPL_JOIN3(data_w,TYPE,_at)(struct librg_data *data, TYPE value, isize position){ ZPL_JOIN3(librg_data_w,TYPE,_at)(data, value, position); }
EXPORT f4mp::Server* server_create(const char* address, i32 port)
{
try
{
return new f4mp::Server(address, port);
}
catch (const std::exception&)
{
return nullptr;
}
}
EXPORT void server_destroy(f4mp::Server* server)
{
delete server;
}
EXPORT void server_start(f4mp::Server* server)
{
server->Start();
}
EXPORT librg_ctx* server_get_ctx(f4mp::Server* server)
{
return server->GetContext();
}
EXPORT void server_tick(f4mp::Server* server)
{
server->Tick();
}
GEN_DATA_READWRITE(i8);
GEN_DATA_READWRITE(u8);
GEN_DATA_READWRITE(i16);
GEN_DATA_READWRITE(u16);
GEN_DATA_READWRITE(i32);
GEN_DATA_READWRITE(u32);
GEN_DATA_READWRITE(i64);
GEN_DATA_READWRITE(u64);
GEN_DATA_READWRITE(f32);
GEN_DATA_READWRITE(f64);
GEN_DATA_READWRITE(b8);
GEN_DATA_READWRITE(b16);
GEN_DATA_READWRITE(b32);
EXPORT void event_add(f4mp::Server* server, u64 id, librg_event_cb* callback)
{
librg_event_add(server->GetContext(), id, callback);
}
EXPORT void network_add(f4mp::Server* server, u16 id, librg_message_cb* callback)
{
librg_network_add(server->GetContext(), id, callback);
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "Server.h"
#if _WIN32
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT
#endif

View file

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{F7829BAB-3A87-4637-91A2-FE95D1650579}</ProjectGuid>
<RootNamespace>f4mpserver</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)\f4mp\;$(IncludePath)</IncludePath>
<OutDir>$(ProjectDir)\scripts\bin\</OutDir>
<TargetName>f4mp</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir)\f4mp\;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBRG_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)\thirdparty\enet\include;$(SolutionDir)\thirdparty\zpl\code;$(SolutionDir)\thirdparty\librg\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)\thirdparty\enet\include;$(SolutionDir)\thirdparty\zpl\code;$(SolutionDir)\thirdparty\librg\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\f4mp\common.cpp" />
<ClCompile Include="api.cpp" />
<ClCompile Include="Character.cpp" />
<ClCompile Include="Entity.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="NPC.cpp" />
<ClCompile Include="Player.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="api.h" />
<ClInclude Include="Character.h" />
<ClInclude Include="Entity.h" />
<ClInclude Include="NPC.h" />
<ClInclude Include="Player.h" />
<ClInclude Include="Server.h" />
<ClInclude Include="server_common.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="소스 파일">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="헤더 파일">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="리소스 파일">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Entity.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Player.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="NPC.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="..\f4mp\common.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="Character.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
<ClCompile Include="api.cpp">
<Filter>소스 파일</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Server.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Entity.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="server_common.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Player.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="NPC.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="Character.h">
<Filter>헤더 파일</Filter>
</ClInclude>
<ClInclude Include="api.h">
<Filter>헤더 파일</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View file

@ -0,0 +1,46 @@
#define LIBRG_IMPLEMENTATION
//#define LIBRG_DISABLE_FEATURE_ENTITY_VISIBILITY
#include "Server.h"
#include <iostream>
#include <fstream>
f4mp::Server* f4mp::Server::instance = nullptr;
int main()
{
const std::string configFilePath = "server_config.txt";
std::string address;
i32 port = 7779;
std::ifstream config(configFilePath);
if (config)
{
config >> address;
config >> port;
config.close();
}
else
{
std::cout << "address? ";
std::cin >> address;
std::ofstream file(configFilePath);
file << address << std::endl << port;
std::cout << std::endl;
}
f4mp::Server* server = new f4mp::Server(address, port);
server->Start();
while (true)
{
server->Tick();
}
return 0;
}

View file

@ -0,0 +1 @@
from .server import Server

View file

@ -0,0 +1,155 @@
import os
import ctypes
from ctypes import CFUNCTYPE, c_char_p, c_uint32, c_void_p, c_uint64
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
dll = ctypes.cdll.LoadLibrary(os.path.join(BASE_DIR, "bin/f4mp.dll"))
class Message(ctypes.Structure):
_fields_ = [
("id", c_uint32),
("ctx", c_void_p),
("data", c_void_p),
("peer", c_void_p),
("packet", c_void_p),
("user_data", c_void_p),
]
class Event(ctypes.Structure):
_fields_ = [
("id", c_uint32),
("ctx", c_void_p),
("data", c_void_p),
("entity", c_void_p),
("peer", c_void_p),
("flags", c_uint64),
("user_data", c_void_p),
]
class Task:
def __init__(self, e_type, event):
self.type = e_type
self.event = Event(event)
class CallbackEnum:
LIBRG_CONNECTION_INIT = 0
LIBRG_CONNECTION_REQUEST = 1
LIBRG_CONNECTION_REFUSE = 2
LIBRG_CONNECTION_ACCEPT = 3
LIBRG_CONNECTION_DISCONNECT = 4
LIBRG_CONNECTION_TIMEOUT = 5
LIBRG_CONNECTION_TIMESYNC = 6
LIBRG_ENTITY_CREATE = 7
LIBRG_ENTITY_UPDATE = 8
LIBRG_ENTITY_REMOVE = 9
LIBRG_CLIENT_STREAMER_ADD = 10
LIBRG_CLIENT_STREAMER_REMOVE = 11
LIBRG_CLIENT_STREAMER_UPDATE = 12
LIBRG_EVENT_LAST = 13
Hit = LIBRG_EVENT_LAST + 1
FireWeapon = LIBRG_EVENT_LAST + 2
SpawnEntity = LIBRG_EVENT_LAST + 3
SyncEntity = LIBRG_EVENT_LAST + 4
SpawnBuilding = LIBRG_EVENT_LAST + 5
RemoveBuilding = LIBRG_EVENT_LAST + 6
Speak = LIBRG_EVENT_LAST + 7
dict = {
"on_connection_request": LIBRG_CONNECTION_REQUEST,
"on_connection_accepted": LIBRG_CONNECTION_ACCEPT,
"on_connection_refused": LIBRG_CONNECTION_REFUSE,
"on_disconnect": LIBRG_CONNECTION_DISCONNECT,
"on_entity_create": LIBRG_ENTITY_CREATE,
"on_entity_delete": LIBRG_ENTITY_REMOVE,
"on_entity_update": LIBRG_ENTITY_UPDATE,
"on_client_update": LIBRG_CLIENT_STREAMER_UPDATE
}
def __getitem__(self, item):
return self.dict[item]
class Callbacks:
def __init__(self, server):
self.server = server
self.instance = server.instance
self.callback_enum = CallbackEnum()
for func_name, enum in self.callback_enum.dict.items():
func = CFUNCTYPE(None, c_void_p)(getattr(self, func_name))
self.server.call_map[func_name] = set()
Interface.register_event(self.instance, enum, func)
def on_connection_request(self, event):
self.server.task_queue.append(Task("on_connection_request", event))
def on_connection_accepted(self, event):
self.server.task_queue.append(Task("on_connection_accepted", event))
def on_connection_refused(self, event):
self.server.task_queue.append(Task("on_connection_refused", event))
def on_disconnect(self, event):
self.server.task_queue.append(Task("on_disconnect", event))
def on_entity_create(self, event):
self.server.task_queue.append(Task("on_entity_create", event))
def on_entity_delete(self, event):
self.server.task_queue.append(Task("on_entity_delete", event))
def on_entity_update(self, event):
self.server.task_queue.append(Task("on_entity_update", event))
def on_client_update(self, event):
self.server.task_queue.append(Task("on_client_update", event))
class Interface:
"""F4MP DLL Interface"""
dll.server_create.restype = c_void_p
dll.server_create.argtypes = c_char_p, c_uint32
@staticmethod
def server_create(address: str, port: int) -> c_void_p:
"""Creates a server
Arguments:
address(str): The bind address for the server
port(int): The port the server will listen on
Note:
The port
"""
return c_void_p(dll.server_create(address.encode(), port))
@staticmethod
def server_start(instance: c_void_p) -> None:
dll.server_start(instance)
@staticmethod
def server_destroy(instance: c_void_p) -> None:
"""Destroys the server"""
dll.server_destroy(instance)
@staticmethod
def server_tick(instance: c_void_p) -> None:
dll.server_tick(instance)
@staticmethod
def register_event(server: c_void_p, enum: int, func):
dll.event_add(server, enum, func)
@staticmethod
def register_net(server: c_void_p, enum: int, func):
dll.network_add(server, enum, func)

View file

@ -0,0 +1,40 @@
import asyncio
from F4MP import f4mp
Interface = f4mp.Interface
class Server:
def __init__(self, address: str, port: int):
self.instance = Interface.server_create(address, port)
self.address = address
self.port = port
self.loop = asyncio.get_event_loop()
self.task_queue = []
self.call_map = {}
self.callbacks = f4mp.Callbacks(self)
def __del__(self):
Interface.server_destroy(self.instance)
def listener(self, name=None):
def decorator(func):
self.call_map[name or func.__name__].add(func)
return func
return decorator
def start(self, tps=20):
Interface.server_start(self.instance)
self.loop_tasks(tps)
def loop_tasks(self, tps):
import time
period = 1 / tps
while True:
start = time.time()
for task in self.task_queue:
for func in self.call_map[task.type]:
func(task.event)
Interface.server_tick(self.instance)
time.sleep(period - max(time.time() - start, 0))

View file

@ -0,0 +1,12 @@
from F4MP import Server
my_server = Server("127.0.0.1", 7779)
@my_server.listener()
def on_connection_request(event):
print("Connection")
my_server.start()

View file

@ -0,0 +1,3 @@
#pragma once
#include "common.h"

View file

@ -0,0 +1,94 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se_loader", "f4se_loader\f4se_loader.vcxproj", "{4229A810-8CB3-4C31-A0F5-8E73D89C0379}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc11", "..\common\common_vc11.vcxproj", "{D4C128A1-73DC-4941-A453-CE55AF239BA8}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se", "f4se\f4se.vcxproj", "{A236F69D-8FF9-4491-AC5F-45BF49448BBE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se_common", "f4se_common\f4se_common.vcxproj", "{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se_steam_loader", "f4se_steam_loader\f4se_steam_loader.vcxproj", "{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se_loader_common", "f4se_loader_common\f4se_loader_common.vcxproj", "{BE858737-949E-48CF-ADF9-079389049B57}"
ProjectSection(ProjectDependencies) = postProject
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6} = {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4mp", "f4mp\f4mp.vcxproj", "{F021FA50-A159-46F9-8455-B079C458E5AF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4mp_server", "f4mp_server\f4mp_server.vcxproj", "{F7829BAB-3A87-4637-91A2-FE95D1650579}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4229A810-8CB3-4C31-A0F5-8E73D89C0379}.Debug|x64.ActiveCfg = Debug|x64
{4229A810-8CB3-4C31-A0F5-8E73D89C0379}.Debug|x64.Build.0 = Debug|x64
{4229A810-8CB3-4C31-A0F5-8E73D89C0379}.Debug|x86.ActiveCfg = Debug|x64
{4229A810-8CB3-4C31-A0F5-8E73D89C0379}.Release|x64.ActiveCfg = Release|x64
{4229A810-8CB3-4C31-A0F5-8E73D89C0379}.Release|x64.Build.0 = Release|x64
{4229A810-8CB3-4C31-A0F5-8E73D89C0379}.Release|x86.ActiveCfg = Release|x64
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x64.ActiveCfg = Debug|x64
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x64.Build.0 = Debug|x64
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x86.ActiveCfg = Debug|Win32
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x86.Build.0 = Debug|Win32
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x64.ActiveCfg = Release|x64
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x64.Build.0 = Release|x64
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x86.ActiveCfg = Release|Win32
{D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x86.Build.0 = Release|Win32
{A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Debug|x64.ActiveCfg = Debug|x64
{A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Debug|x64.Build.0 = Debug|x64
{A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Debug|x86.ActiveCfg = Debug|x64
{A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Release|x64.ActiveCfg = Release|x64
{A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Release|x64.Build.0 = Release|x64
{A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Release|x86.ActiveCfg = Release|x64
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Debug|x64.ActiveCfg = Debug|x64
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Debug|x64.Build.0 = Debug|x64
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Debug|x86.ActiveCfg = Debug|x64
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Release|x64.ActiveCfg = Release|x64
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Release|x64.Build.0 = Release|x64
{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Release|x86.ActiveCfg = Release|x64
{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}.Debug|x64.ActiveCfg = Debug|x64
{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}.Debug|x64.Build.0 = Debug|x64
{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}.Debug|x86.ActiveCfg = Debug|x64
{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}.Release|x64.ActiveCfg = Release|x64
{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}.Release|x64.Build.0 = Release|x64
{B13F31AC-C86C-47FF-B900-EDA1F6EC6C84}.Release|x86.ActiveCfg = Release|x64
{BE858737-949E-48CF-ADF9-079389049B57}.Debug|x64.ActiveCfg = Debug|x64
{BE858737-949E-48CF-ADF9-079389049B57}.Debug|x64.Build.0 = Debug|x64
{BE858737-949E-48CF-ADF9-079389049B57}.Debug|x86.ActiveCfg = Debug|x64
{BE858737-949E-48CF-ADF9-079389049B57}.Release|x64.ActiveCfg = Release|x64
{BE858737-949E-48CF-ADF9-079389049B57}.Release|x64.Build.0 = Release|x64
{BE858737-949E-48CF-ADF9-079389049B57}.Release|x86.ActiveCfg = Release|x64
{F021FA50-A159-46F9-8455-B079C458E5AF}.Debug|x64.ActiveCfg = Debug|x64
{F021FA50-A159-46F9-8455-B079C458E5AF}.Debug|x64.Build.0 = Debug|x64
{F021FA50-A159-46F9-8455-B079C458E5AF}.Debug|x86.ActiveCfg = Debug|Win32
{F021FA50-A159-46F9-8455-B079C458E5AF}.Debug|x86.Build.0 = Debug|Win32
{F021FA50-A159-46F9-8455-B079C458E5AF}.Release|x64.ActiveCfg = Release|x64
{F021FA50-A159-46F9-8455-B079C458E5AF}.Release|x64.Build.0 = Release|x64
{F021FA50-A159-46F9-8455-B079C458E5AF}.Release|x86.ActiveCfg = Release|Win32
{F021FA50-A159-46F9-8455-B079C458E5AF}.Release|x86.Build.0 = Release|Win32
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Debug|x64.ActiveCfg = Debug|x64
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Debug|x64.Build.0 = Debug|x64
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Debug|x86.ActiveCfg = Debug|Win32
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Debug|x86.Build.0 = Debug|Win32
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Release|x64.ActiveCfg = Release|x64
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Release|x64.Build.0 = Release|x64
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Release|x86.ActiveCfg = Release|Win32
{F7829BAB-3A87-4637-91A2-FE95D1650579}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5BBC760F-C0DF-4F62-8684-297A1BEC30CB}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2002-2016 Lee Salzman
Copyright (c) 2017-2018 Vladyslav Hrytsenko, Dominik Madarász
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File diff suppressed because it is too large Load diff

231
f4mp_originalcode/thirdparty/zpl/LICENSE vendored Normal file
View file

@ -0,0 +1,231 @@
This Software is dual licensed under the following licenses:
Unlicense
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
Apache 2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1 @@
*.h linguist-language=C

View file

@ -0,0 +1,19 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_HASHING
#include "zpl.h"
int main(void)
{
char buf[] = "Hello World";
zpl_u8 *enc = zpl_base64_encode(zpl_heap(), buf, zpl_strlen((const char*)buf));
zpl_u8 *dec = zpl_base64_decode(zpl_heap(), enc, zpl_strlen((const char*)enc));
ZPL_ASSERT_NOT_NULL(enc);
ZPL_ASSERT_NOT_NULL(dec);
ZPL_ASSERT_MSG((!zpl_strcmp((const char*)buf, (const char*)dec)), "%s == %s\n", buf, dec);
zpl_printf("Output:\n%s is %s\n", enc, dec);
return 0;
}

View file

@ -0,0 +1,7 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
int main() {
return 0;
}

View file

@ -0,0 +1,7 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NO_MATH_H
#include <zpl.h>
int main() {
return 0;
}

View file

@ -0,0 +1,6 @@
#define ZPL_IMPLEMENTATION
#include <zpl.h>
int main() {
return 0;
}

View file

@ -0,0 +1,6 @@
#define ZPL_IMPLEMENTATION
#include <zpl.h>
int main() {
return 0;
}

View file

@ -0,0 +1,95 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_THREADING
#define ZPL_ENABLE_COROUTINES
#define ZPL_ENABLE_JOBS
#include <zpl.h>
zpl_global zpl_mutex test__print;
void printf_safe(const char *fmt, ...);
typedef struct {
zpl_u32 x;
} some_data;
// this is our meat of co-routine subsystem
void send_req(zpl_co *co) {
some_data *d = cast(some_data*)co->data;
printf_safe("step 1: %d\n", d->x);
zpl_sleep_ms(500);
// wait until main thread resumes the execution
zpl_co_yield(co);
some_data *r = cast(some_data*)co->data;
printf_safe("step 2: no data, we pass some back\n");
r->x = 21;
printf_safe("step 2: guest val %d\n", r->x);
zpl_co_yield(co);
zpl_sleep_ms(500);
d = cast(some_data*)co->data;
printf_safe("step 3: %d\n", d->x);
}
int main (void) {
zpl_mutex_init(&test__print);
// initialize the co-routine subsystem with 4 threads
zpl_co_init(zpl_heap(), 4);
// form a new co-routine (be aware of its lifetime)
zpl_co w1;
zpl_co_make(&w1, send_req);
// create our data
some_data d = {42};
some_data r = {0};
some_data d2 = {0};
// step 1 - start a co-routine, pass data
printf_safe("resume step 1\n");
zpl_co_resume(&w1, cast(void*)&d);
do {} while (!zpl_co_waiting(&w1));
// step 2 - resume its execution, read data back
printf_safe("resume step 2\n");
zpl_co_resume(&w1, cast(void*)&r);
do {} while (!zpl_co_waiting(&w1));
// step 3 - resume its execution again, pass data
printf_safe("resume step 3\n");
zpl_co_resume(&w1, cast(void*)&d2);
do {} while (!zpl_co_waiting(&w1));
d2.x = r.x * 3;
printf_safe("r:%d,d:%d,d2:%d\n", r.x, d.x, d2.x);
// wait until co-routine finishes its execution
do {} while(!zpl_co_finished(&w1));
printf_safe("we're done here!\n");
zpl_co_destroy();
return 0;
}
void printf_safe(const char *fmt, ...) {
zpl_mutex_lock(&test__print);
va_list va;
va_start(va, fmt);
zpl_printf_va(fmt, va);
va_end(va);
zpl_mutex_unlock(&test__print);
}

View file

@ -0,0 +1,24 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include "zpl.h"
#define BUF_LEN 4096
///////////////////////////////////////////////////////////////////////////////////
//
// This demo requires you to have the following packages: "fortune" and "cowsay".
// You should find these in your distro repositories, or
// at source ports sites.
//
int main(void)
{
char buf[BUF_LEN] = {0};
zpl_string text = NULL;
zpl_system_command("fortune | cowsay", BUF_LEN, buf);
text = zpl_system_command_str("fortune | cowsay", zpl_heap());
zpl_printf("Output:\n %s\n\n%s", buf, text);
return 0;
}

View file

@ -0,0 +1,21 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
int main() {
zpl_path_mkdir("./tempdir/", 0733);
zpl_path_mkdir("./tempdir/subdir", 0755);
if (zpl_path_mkdir("./tempdir/", 0733) == ZPL_FILE_ERROR_EXISTS) {
zpl_printf("error: directory already exist\n");
}
if (zpl_path_rmdir("./tempdir") == ZPL_FILE_ERROR_NOT_EMPTY) {
zpl_printf("error: directory is not empty\n");
}
ZPL_ASSERT(zpl_path_rmdir("./tempdir/subdir") == ZPL_FILE_ERROR_NONE);
ZPL_ASSERT(zpl_path_rmdir("./tempdir/") == ZPL_FILE_ERROR_NONE);
return 0;
}

View file

@ -0,0 +1,21 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
#include <string.h>
int main()
{
zpl_string files;
files = zpl_path_dirlist(zpl_heap(), "../misc", true);
char *p=strtok(files, "\n");
while (p) {
printf("%s\n", p);
p=strtok(NULL, "\n");
}
zpl_string_free(files);
return 0;
}

View file

@ -0,0 +1,50 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
char const* get_type(u8 type) {
switch (type) {
case ZPL_DIR_TYPE_FILE:
return "file";
case ZPL_DIR_TYPE_FOLDER:
return "folder";
default:
return "unknown";
}
}
int main() {
zpl_dir_info test={0};
zpl_dirinfo_init(&test, "misc");
for (int i=0; i<zpl_array_count(test.entries); ++i) {
zpl_dir_entry *e = test.entries + i;
printf("entry [%s]: %s\n", get_type(e->type), e->filename);
if (e->type != ZPL_DIR_TYPE_FOLDER) continue;
zpl_dirinfo_step(e);
if (e->dir_info) {
for (int j=0; j<zpl_array_count(e->dir_info->entries); ++j) {
printf("-- subentry [%s]: %s\n", get_type(e->dir_info->entries[j].type), e->dir_info->entries[j].filename);
}
}
}
zpl_dirinfo_free(&test);
printf("Done!\n");
zpl_file foo = {0};
zpl_file_open(&foo, "code/zpl.h");
zpl_file_dirinfo_refresh(&foo);
if (foo.dir) {
for (int j=0; j<zpl_array_count(foo.dir->dir_info->entries); ++j) {
printf("-- file entry: %s\n", foo.dir->dir_info->entries[j].filename);
}
}
zpl_file_close(&foo);
return 0;
}

View file

@ -0,0 +1,20 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
int main()
{
zpl_string filelst;
filelst = zpl_path_dirlist(zpl_heap_allocator(), "$HOME/", true);
char **files=zpl_str_split_lines(zpl_heap_allocator(), filelst, false);
for (i32 i=0; i<zpl_array_count(files); ++i) {
printf("%s\n", files[i]);
}
zpl_string_free(filelst);
zpl_array_free(files);
return 0;
}

View file

@ -0,0 +1,21 @@
#define ZPL_IMPL
#define ZPL_NANO
#include <zpl.h>
#include <stdlib.h>
int main() {
zpl_set_env("ZPLTMP", "Hello World");
const char *foo = zpl_get_env("ZPLTMP");
zpl_printf("%s\n", foo);
zpl_mfree((char *)foo);
const char *foo2 = zpl_get_env_buf("ZPLTMP");
zpl_printf("%s\n", foo2);
zpl_string bar = zpl_get_env_str("ZPLTMP");
zpl_printf("%s\n", bar);
zpl_string_free(bar);
return 0;
}

View file

@ -0,0 +1,48 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_EVENT
#include <zpl.h>
typedef struct {
unsigned long a,b,c;
} test_e1;
enum {
TEST_E1,
};
void test_e1_v1(zpl_event_data evt) {
ZPL_EVENT_CAST(test_e1, data);
zpl_printf("a: %lu b: %lu c: %lu\n", data->a, data->b, data->c);
};
void test_e1_v2(zpl_event_data evt) {
ZPL_EVENT_CAST(test_e1, data);
zpl_printf("a -- %lu b -- %lu c -- %lu\n", data->a, data->b, data->c);
};
void test_e1_v3(zpl_event_data evt) {
ZPL_EVENT_CAST(test_e1, data);
zpl_printf("a> %lu b> %lu c> %lu\n", data->a, data->b, data->c);
};
int main(void) {
zpl_event_pool events;
zpl_event_init(&events, zpl_heap_allocator());
zpl_event_add(&events, TEST_E1, test_e1_v1); // 0
zpl_event_add(&events, TEST_E1, test_e1_v2); // 1
zpl_event_add(&events, TEST_E1, test_e1_v3); // 2
test_e1 cb_data = {1, 2, 3};
zpl_event_trigger(&events, TEST_E1, &cb_data);
zpl_event_remove(&events, TEST_E1, 1);
zpl_printf("After deletion of ID 1\n");
// NOTE(ZaKlaus): Prints 0,2
zpl_event_trigger(&events, TEST_E1, &cb_data);
zpl_event_destroy(&events);
return 0;
}

View file

@ -0,0 +1,69 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include "zpl.h"
////////////////////////////////////////////////////////////
//
// Hash table type and function declaration, call: ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE)
// Hash table function definitions, call: ZPL_TABLE_DEFINE(NAME, FUNC, VALUE)
//
// PREFIX - a prefix for function prototypes e.g. extern, static, etc.
// NAME - Name of the Hash Table
// FUNC - the name will prefix function names
// VALUE - the type of the value to be stored
//
// NOTE(ZaKlaus): Declare user structure
typedef struct user {
zpl_string name;
i64 score;
} user;
// NOTE(ZaKlaus): Declare hash table of type user
ZPL_TABLE_DECLARE(extern, userbl, tbl_user_, user);
// NOTE(ZaKlaus): Define hash table functions
// Place exactly in __ONE__ source file!
ZPL_TABLE_DEFINE(userbl, tbl_user_, user);
int
main(void) {
// NOTE(ZaKlaus): Create new hash table
userbl users;
tbl_user_init(&users, zpl_heap_allocator());
// NOTE(ZaKlaus): Create hash key
u64 key = 0x29;
// NOTE(ZaKlaus): Create new user
user user1;
user1.name = zpl_string_make(zpl_heap_allocator(), "John Doe");
user1.score = 2674;
// NOTE(ZaKlaus): Add user to the hash table
tbl_user_set(&users, key, user1);
// NOTE(ZaKlaus): Create another user
user user2;
user2.name = zpl_string_make(zpl_heap_allocator(), "Steve L.");
user2.score = 303312;
// NOTE(ZaKlaus): Add user to the hash table
tbl_user_set(&users, key+0xDEADBEEF, user2);
// NOTE(ZaKlaus): Retrieve the first user
user user3 = *tbl_user_get(&users, key);
// NOTE(ZaKlaus): Make sure the user is identical to the first user
ZPL_ASSERT(user3.score == user1.score);
// NOTE(ZaKlaus): Release resources
zpl_string_free(user1.name);
zpl_string_free(user2.name);
tbl_user_destroy(&users);
return 0;
}

View file

@ -0,0 +1,57 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_THREADING
#define ZPL_ENABLE_JOBS
#include <zpl.h>
#define TEST_ENQUEUE_JOB 0.8
zpl_mutex print_mut;
void calc_nums(void* data) {
zpl_unused(data);
i64 nums=0;
zpl_random rnd={0};
zpl_random_init(&rnd);
for (int i=0; i<100; ++i) {
nums+=(zpl_random_gen_u64(&rnd) & 100);
}
//zpl_sleep_ms(50*zpl_random_range_i64(&rnd, 2, 8));
zpl_mutex_lock(&print_mut);
zpl_printf("Result is: %ld\n", (long)nums);
zpl_mutex_unlock(&print_mut);
}
int main() {
zpl_random rng={0};
zpl_thread_pool p={0};
zpl_jobs_init(&p, zpl_heap(), 2);
zpl_random_init(&rng);
zpl_mutex_init(&print_mut);
zpl_jobs_enqueue(&p, calc_nums, NULL);
zpl_jobs_enqueue(&p, calc_nums, NULL);
zpl_jobs_enqueue(&p, calc_nums, NULL);
zpl_jobs_enqueue(&p, calc_nums, NULL);
zpl_jobs_enqueue(&p, calc_nums, NULL);
zpl_jobs_enqueue(&p, calc_nums, NULL);
zpl_jobs_enqueue(&p, calc_nums, NULL);
f64 time=zpl_time_now();
for (;;) {
f64 now=zpl_time_now();
f64 dt =now-time;
if (dt > TEST_ENQUEUE_JOB) {
time=now;
zpl_jobs_enqueue(&p, calc_nums, NULL);
}
zpl_jobs_process(&p);
}
return 0;
}

View file

@ -0,0 +1,54 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_JSON
#include <zpl.h>
int main(void) {
zpl_file_contents fc;
fc = zpl_file_read_contents(zpl_heap(), true, "misc/data/test.json5");
zpl_json_object root = {0};
u8 err;
zpl_json_parse(&root, fc.size, (char *)fc.data, zpl_heap_allocator(), true, &err);
zpl_json_object *replace = NULL;
replace = zpl_json_find(&root, "replace_me", false);
if (replace != NULL)
{
zpl_printf("Field was found! Current value: %ld\nReplacing with an array!\n", (long)replace->integer);
replace->type = ZPL_JSON_TYPE_ARRAY;
zpl_array_init(replace->nodes, replace->backing);
for (size_t i = 0; i < 5; i++)
{
zpl_json_object *o = zpl_json_add(replace, NULL, ZPL_JSON_TYPE_INTEGER);
if (o) {
o->integer = (i64)i+1;
}
}
replace->name = "i_am_replaced ";
}
zpl_json_object *first = zpl_json_add_at(&root, 0, "first", ZPL_JSON_TYPE_STRING);
if (first)
{
first->string = "I am first!";
first->assign_style = ZPL_JSON_ASSIGN_STYLE_EQUALS;
}
zpl_printf("Error code: %d\n", err);
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &root, 0);
zpl_json_free(&root);
zpl_file_free_contents(&fc);
return 0;
}

View file

@ -0,0 +1,70 @@
//
// Recommends presence of jeopardy.json file, which can be received at: https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
//
// Stats on i7-4790k with jeopardy.json file
// Parsing - ~160 ms
// Parsing with strip_comments enabled - ~255 ms
//
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_JSON
#define ZPL_ENABLE_OPTS
#include <zpl.h>
int main(int argc, char **argv) {
zpl_opts opts={0};
zpl_opts_init(&opts, zpl_heap(), argv[0]);
zpl_opts_add(&opts, "f", "file", "input file name.", ZPL_OPTS_STRING);
zpl_opts_add(&opts, "s", "strip-comments", "strip comments from JSON file (recommended).", ZPL_OPTS_FLAG);
zpl_opts_positional_add(&opts, "file");
b32 ok = zpl_opts_compile(&opts, argc, argv);
char *filename = NULL;
b32 strip_comments = false;
if (ok && zpl_opts_positionals_filled(&opts))
{
filename = zpl_opts_string(&opts, "file", NULL);
strip_comments = zpl_opts_has_arg(&opts, "strip-comments");
if (filename == NULL)
goto bad;
zpl_printf("Filename: %s\n", filename);
}
else
{
bad:
zpl_opts_print_errors(&opts);
zpl_opts_print_help(&opts);
return -1;
}
zpl_file_contents fc = zpl_file_read_contents(zpl_heap(), true, filename);
zpl_printf("Parsing JSON5 file!\n");
zpl_json_object root = {0};
u8 err;
f64 time = zpl_time_now();
zpl_json_parse(&root, fc.size, (char *)fc.data, zpl_heap(), strip_comments, &err);
f64 delta = zpl_time_now() - time;
if (err == ZPL_JSON_ERROR_OBJECT_OR_SOURCE_WAS_NULL)
{
zpl_printf("File not found!\n");
return -2;
}
zpl_printf("Delta: %fms\nNo. of nodes: %td\nError code: %d\nFile size: %td bytes\n", delta*1000, zpl_array_count(root.nodes), err, fc.size);
zpl_json_free(&root);
zpl_file_free_contents(&fc);
return 0;
}

View file

@ -0,0 +1,30 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_JSON
#include <zpl.h>
int main(void) {
zpl_json_object root = {0};
root.cfg_mode = 1;
zpl_json_init_node(&root, zpl_heap(), "<root>" /* unused for root object */, ZPL_JSON_TYPE_OBJECT);
zpl_random rng={0};
zpl_random_init(&rng);
for (size_t i = 0; i < 16000000; i++)
{
zpl_json_object *o = zpl_json_add(&root, zpl_string_sprintf_buf(zpl_heap(), "field%lld\t", i), ZPL_JSON_TYPE_INTEGER);
if (o) {
o->name_style = ZPL_JSON_NAME_STYLE_NO_QUOTES;
o->assign_style = ZPL_JSON_ASSIGN_STYLE_LINE;
o->delim_style = ZPL_JSON_DELIM_STYLE_NEWLINE;
o->integer = zpl_random_gen_u64(&rng) & i;
}
}
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &root, 0);
zpl_json_free(&root);
return 0;
}

View file

@ -0,0 +1,29 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include "zpl.h"
int main(void)
{
zpl_list s, *head, *cursor;
zpl_list_init(&s, "it is optional to call init: ");
head = cursor = &s;
// since we can construct an element implicitly this way
// the second field gets overwritten once we add it to a list.
zpl_list a = {"hello"};
cursor = zpl_list_add(cursor, &a);
zpl_list b = {"world"};
cursor = zpl_list_add(cursor, &b);
zpl_list c = {"!!! OK"};
cursor = zpl_list_add(cursor, &c);
for (zpl_list *l=head; l; l=l->next) {
zpl_printf("%s ", cast(char *)l->ptr);
}
zpl_printf("\n");
return 0;
}

View file

@ -0,0 +1,18 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_MATH
#include <zpl.h>
int main(void) {
zpl_vec3 myvec = zpl_vec3f(10.0f, 5.0f, 0.0f);
myvec *= 5.0f;
ZPL_ASSERT(myvec.x == 50.0f);
ZPL_ASSERT(myvec.y == 25.0f);
ZPL_ASSERT(myvec.z == 0.0f);
zpl_printf("result vec: %f %f %f\n", myvec.x, myvec.y, myvec.z);
return 0;
}

View file

@ -0,0 +1,50 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_OPTS
#include <zpl.h>
int main(int argc, char **argv)
{
zpl_opts opts={0};
zpl_opts_init(&opts, zpl_heap(), argv[0]);
zpl_opts_add(&opts, "?", "help", "the HELP section", ZPL_OPTS_FLAG);
zpl_opts_add(&opts, "f", "foo", "the test *foo* entry.", ZPL_OPTS_STRING);
zpl_opts_add(&opts, "p", "pi", "PI Value Redefined !!!", ZPL_OPTS_FLOAT);
zpl_opts_add(&opts, "4", "4pay", "hmmmm", ZPL_OPTS_INT);
zpl_opts_add(&opts, "E", "enablegfx", "Enables HD resource pack", ZPL_OPTS_FLAG);
zpl_opts_positional_add(&opts, "4pay");
b32 ok=zpl_opts_compile(&opts, argc, argv);
if (ok && zpl_opts_positionals_filled(&opts)) {
b32 help=zpl_opts_has_arg(&opts, "help");
if (help) {
zpl_opts_print_help(&opts);
return 0;
}
zpl_string foo=zpl_opts_string(&opts, "foo", "WRONG!");
f64 some_num=zpl_opts_real(&opts, "pi", 0.0);
i32 right=(i32)zpl_opts_integer(&opts, "4pay", 42);
zpl_printf("The arg is %s\nPI value is: %f\nright: %d?\n", foo, some_num,
right);
b32 gfx=zpl_opts_has_arg(&opts, "enablegfx");
if (gfx) {
zpl_printf("You wanted HD graphics? Here:\n\n");
for (int i=0; i<5; ++i) {
zpl_printf("iiiii\n");
}
}
}
else {
zpl_opts_print_errors(&opts);
zpl_opts_print_help(&opts);
}
return 0;
}

View file

@ -0,0 +1,36 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include "zpl.h"
int
main(void) {
#define ARRAY_SIZE 4096*8
// NOTE(ZaKlaus): Define a storage for our numbers and the temporary buffer.
u32 nums[ARRAY_SIZE] = {0};
u32 temp[ARRAY_SIZE] = {0};
zpl_f64_cmp(0);
// NOTE(ZaKlaus): Initialize random seed.
// and generate random values.
zpl_random r; zpl_random_init(&r);
for (int i = 0; i < ARRAY_SIZE; ++i) {
nums[i] = zpl_random_gen_u32_unique(&r);
}
// NOTE(ZaKlaus): Print these values out
zpl_printf("First 30 numbers...\nBefore: ");
for (int i = 0; i < 90; ++i) {
zpl_printf("%d, ", nums[i]);
}
// NOTE(ZaKlaus): Use radix sort on our numbers and print them out again.
zpl_radix_sort_u32(nums, temp, ARRAY_SIZE);
zpl_printf("\n\nAfter: ");
for (int i = 0; i < 90; ++i) {
zpl_printf("%d, ", nums[i]);
}
return 0;
}

View file

@ -0,0 +1,31 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_REGEX
#include <zpl.h>
int main(void) {
zpl_re re={0};
char const* pat = "(//<<([\\w._]+)>>)";
u32 err = zpl_re_compile(&re, zpl_heap(), pat, zpl_strlen(pat));
if (err) {
zpl_printf("Regex pattern is invalid! Error code: %d\n", err);
}
zpl_printf("Regex pattern was compiled!\n");
char const* str = "//<<buffer.c>> aaa //<<hello.c>>\n aa//<<aaa.c>>//<<test.c>>";
zpl_array_make(zpl_re_capture, captures, zpl_heap());
zpl_re_match_all(&re, str, zpl_strlen(str), 2, &captures);
for (isize i=0; i < zpl_array_count(captures); i++)
{
zpl_printf("Group %ld: %.*s\n", i, (i32)captures[i].len, captures[i].str);
}
return 0;
}

View file

@ -0,0 +1,24 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
ZPL_RING_DECLARE(u32);
ZPL_RING_DEFINE(u32);
int main()
{
zpl_ring_u32 pad={0};
zpl_ring_u32_init(&pad, zpl_heap(), 3);
zpl_ring_u32_append(&pad, 1);
zpl_ring_u32_append(&pad, 2);
zpl_ring_u32_append(&pad, 3);
while (!zpl_ring_u32_empty(&pad)) {
zpl_printf("Result is %d\n", *zpl_ring_u32_get(&pad));
}
zpl_ring_u32_free(&pad);
return 0;
}

View file

@ -0,0 +1,45 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include "zpl.h"
int
main(void) {
// NOTE(ZaKlaus): Create stack memory
zpl_stack_memory stack = {0};
zpl_stack_memory_init(&stack, zpl_heap_allocator(), 2);
zpl_allocator stack_alloc = zpl_stack_allocator(&stack);
// NOTE(ZaKlaus): Create array container
zpl_array(u32) arr;
zpl_array_init(arr, stack_alloc);
// NOTE(ZaKlaus): Push 5 elements
for (i32 i = 0; i < 5; ++i) {
zpl_array_append(arr, i*2);
}
// NOTE(ZaKlaus): List them
zpl_printf("Before removal:\n");
for (i32 i = 0; i < 5; ++i) {
zpl_printf("Value: %d\n", arr[i]);
}
// NOTE(ZaKlaus): Pop 2 values
zpl_array_pop(arr);
zpl_array_pop(arr);
// NOTE(ZaKlaus): List them again
zpl_printf("\nAfter removal:\n");
for (i32 i = 0; i < 3; ++i) {
zpl_printf("Value: %d\n", arr[i]);
}
// NOTE(ZaKlaus): Clear the array out
zpl_array_clear(arr);
// NOTE(ZaKlaus): Release used resources
zpl_stack_memory_free(&stack);
return 0;
}

View file

@ -0,0 +1,16 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
int main()
{
char const* foo = "hello world";
//NOTE: length of a string + 1 extra byte for null terminator.
isize len = strlen(foo) + 1;
char *bar = zpl_strdup(zpl_heap(), (char *)foo, len);
zpl_printf("%s == %s\n", foo, bar);
return 0;
}

View file

@ -0,0 +1,47 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_TIMER
#include <zpl.h>
zpl_global u8 counter = 0;
void test1_cb(void *user_data) {
(void)user_data;
zpl_printf("Hello, Sailor!\n");
if (counter == 10) {
zpl_printf("The second timer is done, press ^C to terminate the process.\n");
}
}
void test2_cb(void *user_data) {
(void)user_data;
zpl_printf("This has been called %d/10 times!\n", ++counter);
}
int main(void) {
zpl_timer_pool timers;
zpl_array_init(timers, zpl_heap_allocator());
zpl_timer *t1 = zpl_timer_add(timers);
zpl_timer_set(t1, 4, -1, test1_cb);
zpl_timer_start(t1, 0);
zpl_timer *t2 = zpl_timer_add(timers);
zpl_timer_set(t2, 1, 10, test2_cb);
zpl_timer_start(t2, 2);
zpl_timer *t3 = zpl_timer_add(timers);
zpl_timer_set(t3, 1, 10, test2_cb);
zpl_timer_start(t3, 2);
zpl_timer_stop(t3); // NOTE(ZaKlaus): This won't be fired!
while (1) {
zpl_timer_update(timers);
}
zpl_array_free(timers);
return 0;
}

View file

@ -0,0 +1,41 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#include <zpl.h>
void easy_print(i64 *nums, isize cnt) {
zpl_printf("Numbers:\n");
for (isize i = 0; i < cnt; ++i) {
zpl_when (&nums[i], i64*, number) {
zpl_printf("%ld, ", (long)*number);
}
}
zpl_printf("\n");
}
int main(void) {
zpl_random rng = {0};
zpl_random_init(&rng);
// NOTE(ZaKlaus): Initialize our buffer
zpl_buffer(i64) numbers = 0;
zpl_buffer_init(numbers, zpl_heap_allocator(), zpl_size_of(i64)*20);
for (isize i = 0; i < 20; ++i) {
numbers[i] = zpl_random_range_i64(&rng, 0, 2);
}
easy_print(numbers, 20);
// NOTE(ZaKlaus): Perform checks
for (isize i = 0; i < 20; ++i) {
if (i == 19) continue;
zpl_when (&numbers[i], i64*, number) {
*number = *number + *(number+1);;
}
}
easy_print(numbers, 20);
return 0;
}

View file

@ -0,0 +1,60 @@
#include "tester_framework.h"
#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
#pragma GCC diagnostic ignored "-Wunused-value"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wwrite-strings"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#pragma GCC diagnostic ignored "-Wmissing-braces"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4201)
#pragma warning(disable : 4127) // Conditional expression is constant
#endif
/* TEST CATEGORIES */
#include "unit/json.h"
#include "unit/print.h"
#define MODULE_LIST \
X(json5_parser) \
X(print)
int main() {
int err = 0, cnt = 0;
zpl_printf("ZPL Automated tests\n");
#define X(name) cnt++;
MODULE_LIST
#undef X
zpl_printf("Modules to be tested: %d\n", cnt);
#define X(name) err += ZPL_JOIN2(module__,name)();
MODULE_LIST
#undef X
fflush(stdout);
zpl_printf("--------------------------------------\n");
zpl_printf("MODULES: %d total, %d failed, %d passed\n", _g_modules, _g_modules_err, _g_modules - _g_modules_err);
zpl_printf("TESTS: %d total, %d failed, %d passed\n", _g_total, _g_errors, _g_total - _g_errors);
return -err;
}
#if defined(ZPL_COMPILER_MSVC)
#pragma warning(pop)
#endif
#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif

View file

@ -0,0 +1,44 @@
#define ZPL_IMPL
#include <zpl.h>
static int _g_modules = 0;
static int _g_modules_err = 0;
static int _g_total = 0;
static int _g_errors = 0;
#define MODULE(name, scope) \
int ZPL_JOIN2(module__,name)() { \
zpl_printf("--------------------------------------\n"); \
zpl_printf(" MODULE: %s\n", #name); \
fflush(stdout); \
_g_modules++; \
int _total = 0; \
int _errors = 0; \
int _lasterr = 0; \
char *_errstr = 0; \
scope; \
fflush(stdout); \
zpl_printf(" Results: %d total, %d failed, %d passed\n", _total, _errors, _total - _errors); \
_g_total += _total; \
_g_errors += _errors; \
if (_errors) _g_modules_err++; \
return (_errors); \
}
#define IT(desc, scope) \
_lasterr = 0; \
_errstr = ""; \
_total += 1; \
do scope while(0); \
_errors += _lasterr; \
printf(" TEST: %-80s: %s%s\n", desc, (_lasterr) ? "\x1B[31mFAIL\x1B[0m" : "\x1B[32mPASS\x1B[0m", _errstr);
#define FAIL(a, b) { _errstr = zpl_bprintf("\n Reason: %s:%d %s %s:%d\n", #a, (int)a, (a == b)?"==":"!=", #b, b); _lasterr = 1; break; }
#define STRFAIL(a, b) { _errstr = zpl_bprintf("\n Reason: %s:%s %s %s:%s\n", #a, (char *)a, (!strcmp(a,b))?"==":"!=", #b, b); _lasterr = 1; break; }
#define EQUALS(a, b) if (a != b) { FAIL(a, b); }
#define STREQUALS(a, b) if (!!strcmp(a,b)) { STRFAIL(a, b); }
#define STRNEQUALS(a, b) if (!strcmp(a,b)) { STRFAIL(a, b); }
#define NEQUALS(a, b) if (a == b) { FAIL(a, b); }
#define LESSER(a, b) if (a >= b) { FAIL(a, b); }
#define GREATER(a, b) if (a <=b) { FAIL(a, b); }
#define LESSEREQ(a, b) if (a < b) { FAIL(a, b); }
#define GREATEREQ(a, b) if (a > b) { FAIL(a, b); }

View file

@ -0,0 +1,242 @@
#define __PARSE(comments) \
zpl_json_object r={0}; \
zpl_json_parse(&r, zpl_strlen(t), (char *const)t, zpl_heap(), comments, &err);
MODULE(json5_parser, {
zpl_u8 err = 0;
IT("parses empty JSON5 object", {
const char *t = "{}";
__PARSE(false);
EQUALS(err, 0);
EQUALS(zpl_array_count(r.nodes), 0);
EQUALS(r.type, ZPL_JSON_TYPE_OBJECT);
zpl_json_free(&r);
});
IT("parses empty JSON5 array", {
const char *t = " [ ]";
__PARSE(false);
EQUALS(err, 0);
EQUALS(r.type, ZPL_JSON_TYPE_ARRAY);
zpl_json_free(&r);
});
IT("fails to parse broken JSON5 array", {
const char *t = "[ }";
__PARSE(false);
EQUALS(err, ZPL_JSON_ERROR_INVALID_VALUE);
zpl_json_free(&r);
});
IT("fails to parse broken JSON5 object", {
const char *t = "{ \t]";
__PARSE(false);
EQUALS(err, ZPL_JSON_ERROR_INVALID_VALUE);
zpl_json_free(&r);
});
IT("fails to parse invalid data", {
const char *t = "{{jsdljsdksd{}}{]][{}";
__PARSE(false);
EQUALS(err, ZPL_JSON_ERROR_INVALID_VALUE);
zpl_json_free(&r);
});
IT("parses commented JSON5 object", {
// const char *t = "{/* TEST CODE */ \"a\": 123 }";
zpl_string t = zpl_string_make(zpl_heap(), "{/* TEST CODE */ \"a\": 123 }");
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
NEQUALS(zpl_array_count(r.nodes), 0);
EQUALS(r.nodes[0].type, ZPL_JSON_TYPE_INTEGER);
EQUALS(r.nodes[0].integer, 123);
zpl_json_free(&r);
zpl_string_free(t);
});
IT("parses commented JSON5 array", {
zpl_string t = zpl_string_make(zpl_heap(), "[/* TEST CODE */ 123 ]");
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
NEQUALS(zpl_array_count(r.nodes), 0);
EQUALS(r.nodes[0].type, ZPL_JSON_TYPE_INTEGER);
EQUALS(r.nodes[0].integer, 123);
zpl_json_free(&r);
zpl_string_free(t);
});
IT("parses JSON array of multiple values", {
zpl_string t = zpl_string_make(zpl_heap(), "[ 123, 456, `hello` ]");
__PARSE(false);
EQUALS(err, ZPL_JSON_ERROR_NONE);
EQUALS(zpl_array_count(r.nodes), 3);
EQUALS(r.nodes[0].integer, 123);
EQUALS(r.nodes[1].integer, 456);
STREQUALS(r.nodes[2].string, "hello");
zpl_json_free(&r);
zpl_string_free(t);
});
IT("parses commented JSON5 document", {
zpl_string t = zpl_string_make(zpl_heap(), ZPL_MULTILINE(\
{
$api: "opengl",
name: "Diffuse shader",
version: "150",
type: "fragment",
// These are shader uniforms
uniforms: [
{ name: 'l_pos', type: 'vec3'},
{ name: 'l_mat', type: 'mat4'},
],
_meta: "0 0 -34 2.34 123 2.34e-4",
/* GLSL shader code */
code: `
uniform vec3 l_pos;
uniform mat4 l_mat;
void main() {
// ...
}
`,
}));
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
EQUALS(zpl_array_count(r.nodes), 7);
zpl_string_free(t);
zpl_json_free(&r);
});
IT("parses nested array", {
const char *t = ZPL_MULTILINE(\
[
[
[
]
]
]);
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
zpl_json_free(&r);
});
IT("parses nested array inside of an object", {
zpl_string t = zpl_string_make(zpl_heap(), ZPL_MULTILINE(\
{ "foo": [
[
[
]
]
]}));
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
zpl_string_free(t);
zpl_json_free(&r);
});
IT("parses keywords", {
zpl_string t = zpl_string_make(zpl_heap(), ZPL_MULTILINE(\
[
true,
false,
null,
Infinity,
-Infinity,
NaN,
-NaN
]));
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
zpl_string_free(t);
zpl_json_free(&r);
});
IT("parses empty object as a field", {
zpl_string t = zpl_string_make(zpl_heap(), ZPL_MULTILINE(\
{
"foo": {}
}));
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
zpl_string_free(t);
zpl_json_free(&r);
});
IT("parses numbers with scientific notation", {
zpl_string t = zpl_string_make(zpl_heap(), ZPL_MULTILINE(\
{
"foo": 1.e-2,
"bar": 42.23e4,
"baz": .032
}));
__PARSE(true);
EQUALS(err, ZPL_JSON_ERROR_NONE);
EQUALS(r.nodes[0].base, 1);
EQUALS(r.nodes[0].exp, 2);
EQUALS(r.nodes[0].exp_neg, true);
EQUALS(r.nodes[1].base, 42);
EQUALS(r.nodes[1].base2, 23);
EQUALS(r.nodes[1].base2_offset, 0);
EQUALS(r.nodes[1].exp, 4);
EQUALS(r.nodes[2].base2, 32);
EQUALS(r.nodes[2].base2_offset, 1);
EQUALS(r.nodes[2].lead_digit, false);
zpl_string_free(t);
zpl_json_free(&r);
});
IT("parses minified JSON array", {
zpl_string t = zpl_string_make(zpl_heap(), "[{\"Name\":\"ATLAS0.png\",\"Width\":256,\"Height\":128,\"Images\":[{\"Name\":\"4\",\"X\":0,\"Y\":0,\"Width\":40,\"Height\":27,\"FrameX\":0,\"FrameY\":0,\"FrameW\":40,\"FrameH\":27},{\"Name\":\"0\",\"X\":41,\"Y\":0,\"Width\":40,\"Height\":27,\"FrameX\":0,\"FrameY\":0,\"FrameW\":40,\"FrameH\":27},{\"Name\":\"6\",\"X\":82,\"Y\":0,\"Width\":33,\"Height\":35,\"FrameX\":0,\"FrameY\":0,\"FrameW\":33,\"FrameH\":35},{\"Name\":\"2\",\"X\":0,\"Y\":28,\"Width\":33,\"Height\":35,\"FrameX\":0,\"FrameY\":0,\"FrameW\":33,\"FrameH\":35},{\"Name\":\"7\",\"X\":36,\"Y\":28,\"Width\":31,\"Height\":38,\"FrameX\":0,\"FrameY\":0,\"FrameW\":31,\"FrameH\":38},{\"Name\":\"3\",\"X\":118,\"Y\":0,\"Width\":31,\"Height\":38,\"FrameX\":0,\"FrameY\":0,\"FrameW\":31,\"FrameH\":38},{\"Name\":\"5\",\"X\":157,\"Y\":0,\"Width\":37,\"Height\":34,\"FrameX\":0,\"FrameY\":0,\"FrameW\":37,\"FrameH\":34},{\"Name\":\"1\",\"X\":118,\"Y\":32,\"Width\":37,\"Height\":34,\"FrameX\":0,\"FrameY\":0,\"FrameW\":37,\"FrameH\":34}],\"IsRotated\":true,\"IsTrimmed\":false,\"IsPremultiplied\":false}]");
__PARSE(false);
EQUALS(err, ZPL_JSON_ERROR_NONE);
EQUALS(zpl_array_count(r.nodes), 1);
EQUALS(zpl_array_count(r.nodes[0].nodes), 7);
EQUALS(r.nodes[0].nodes[3].type, ZPL_JSON_TYPE_ARRAY);
EQUALS(zpl_array_count(r.nodes[0].nodes[3].nodes), 8);
zpl_json_free(&r);
});
IT("parses geojson.io data", {
zpl_string t = zpl_string_make(zpl_heap(), "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[19.09149169921875,48.55786390423251],[19.172515869140625,48.55786390423251],[19.172515869140625,48.60101970261553],[19.09149169921875,48.60101970261553],[19.09149169921875,48.55786390423251]]]}},{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[19.149169921875,48.56024979174329],[16.63330078125,49.160154652338015],[18.28125,49.82380908513249],[18.720703125,49.210420445650286],[19.62158203125,48.929717630629554],[19.13818359375,48.58205840283824]]}},{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"Point\",\"coordinates\":[17.962646484375,48.17341248658084]}},{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[18.4130859375,47.68388118858139],[19.951171875,47.00647991252098],[20.819091796874996,47.83159592699297],[19.237060546875,48.19904897935913],[18.380126953125,48.06706753191901],[17.633056640625,47.67648444221321],[17.764892578124996,47.21583707523794],[18.4130859375,47.68388118858139]]]}}]}");
__PARSE(false);
EQUALS(err, ZPL_JSON_ERROR_NONE);
NEQUALS(zpl_array_count(r.nodes), 0);
zpl_string_free(t);
zpl_json_free(&r);
});
});

View file

@ -0,0 +1,3 @@
MODULE(print, {
});

View file

@ -0,0 +1,192 @@
// file: header/core/collections/array.h
////////////////////////////////////////////////////////////////
//
// Dynamic Array (POD Types)
//
// zpl_array(Type) works like zpl_string or zpl_buffer where the actual type is just a pointer to the first
// element.
//
// Available Procedures for zpl_array(Type)
// zpl_array_init
// zpl_array_free
// zpl_array_set_capacity
// zpl_array_grow
// zpl_array_append
// zpl_array_appendv
// zpl_array_pop
// zpl_array_clear
// zpl_array_back
// zpl_array_front
// zpl_array_resize
// zpl_array_reserve
//
#if 0 // Example
void foo(void) {
zpl_isize i;
int test_values[] = {4, 2, 1, 7};
zpl_allocator a = zpl_heap_allocator();
zpl_array(int) items;
zpl_array_init(items, a);
zpl_array_append(items, 1);
zpl_array_append(items, 4);
zpl_array_append(items, 9);
zpl_array_append(items, 16);
items[1] = 3; // Manually set value
// NOTE: No array bounds checking
for (i = 0; i < items.count; i++)
zpl_printf("%d\n", items[i]);
// 1
// 3
// 9
// 16
zpl_array_clear(items);
zpl_array_appendv(items, test_values, zpl_count_of(test_values));
for (i = 0; i < items.count; i++)
zpl_printf("%d\n", items[i]);
// 4
// 2
// 1
// 7
zpl_array_free(items);
}
#endif
#ifdef ZPL_EDITOR
#include <zpl.h>
#endif
ZPL_BEGIN_C_DECLS
typedef struct zpl_array_header {
char *data;
zpl_isize count;
zpl_isize capacity;
zpl_allocator allocator;
} zpl_array_header;
#define zpl_array(Type) Type *
#define zpl_array_make(Type, Name, allocator) Type *Name; zpl_array_init(Name, allocator)
#ifndef ZPL_ARRAY_GROW_FORMULA
#define ZPL_ARRAY_GROW_FORMULA(x) (2 * (x) + 8)
#endif
ZPL_STATIC_ASSERT(ZPL_ARRAY_GROW_FORMULA(0) > 0, "ZPL_ARRAY_GROW_FORMULA(0) <= 0");
#define ZPL_ARRAY_HEADER(x) (cast(zpl_array_header *)(x) - 1)
#define zpl_array_allocator(x) (ZPL_ARRAY_HEADER(x)->allocator)
#define zpl_array_count(x) (ZPL_ARRAY_HEADER(x)->count)
#define zpl_array_capacity(x) (ZPL_ARRAY_HEADER(x)->capacity)
#define zpl_array_end(x) (x + (zpl_array_count(x) - 1))
#define zpl_array_init_reserve(x, allocator_, cap) \
do { \
void **zpl__array_ = cast(void **) & (x); \
zpl_array_header *zpl__ah = \
cast(zpl_array_header *) zpl_alloc(allocator_, zpl_size_of(zpl_array_header) + zpl_size_of(*(x)) * (cap)); \
zpl__ah->allocator = allocator_; \
zpl__ah->count = 0; \
zpl__ah->data = (char *)x; \
zpl__ah->capacity = cap; \
*zpl__array_ = cast(void *)(zpl__ah + 1); \
} while (0)
// NOTE: Give it an initial default capacity
#define zpl_array_init(x, allocator) zpl_array_init_reserve(x, allocator, ZPL_ARRAY_GROW_FORMULA(0))
#define zpl_array_free(x) \
do { \
zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \
zpl_free(zpl__ah->allocator, zpl__ah); \
} while (0)
#define zpl_array_set_capacity(x, capacity) \
do { \
if (x) { \
void **zpl__array_ = cast(void **) & (x); \
*zpl__array_ = zpl__array_set_capacity((x), (capacity), zpl_size_of(*(x))); \
} \
} while (0)
// NOTE: Do not use the thing below directly, use the macro
ZPL_DEF void *zpl__array_set_capacity(void *array, zpl_isize capacity, zpl_isize element_size);
#define zpl_array_grow(x, min_capacity) \
do { \
zpl_isize new_capacity = ZPL_ARRAY_GROW_FORMULA(zpl_array_capacity(x)); \
if (new_capacity < (min_capacity)) new_capacity = (min_capacity); \
zpl_array_set_capacity(x, new_capacity); \
} while (0)
#define zpl_array_append(x, item) \
do { \
if (zpl_array_capacity(x) < zpl_array_count(x) + 1) zpl_array_grow(x, 0); \
(x)[zpl_array_count(x)++] = (item); \
} while (0)
#define zpl_array_append_at(x, item, ind) \
do { \
zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \
if (ind == zpl__ah->count) { zpl_array_append(x, item); break; } \
if (zpl_array_capacity(x) < zpl_array_count(x) + 1) zpl_array_grow(x, 0); \
zpl_memmove(&(x)[ind + 1], (x + ind), zpl_size_of(x[0]) * (zpl__ah->count - ind)); \
x[ind] = item; \
zpl__ah->count++; \
} while (0)
#define zpl_array_appendv(x, items, item_count) \
do { \
zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \
ZPL_ASSERT(zpl_size_of((items)[0]) == zpl_size_of((x)[0])); \
if (zpl__ah->capacity < zpl__ah->count + (item_count)) zpl_array_grow(x, zpl__ah->count + (item_count)); \
zpl_memcopy(&(x)[zpl__ah->count], (items), zpl_size_of((x)[0]) * (item_count)); \
zpl__ah->count += (item_count); \
} while (0)
#define zpl_array_remove_at(x, index) \
do { \
zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \
ZPL_ASSERT(index < zpl__ah->count); \
zpl_memmove(x + index, x + index + 1, zpl_size_of(x[0]) * (zpl__ah->count - index)); \
--zpl__ah->count; \
} while (0)
#define zpl_array_copy_init(y, x) \
do { \
zpl_array_init_reserve(y, zpl_array_allocator(x), zpl_array_capacity(x)); \
zpl_memcopy(y, x, zpl_array_capacity(x) * zpl_size_of(*x)); \
zpl_array_count(y) = zpl_array_count(x); \
} while (0)
#define zpl_array_pop(x) \
do { \
ZPL_ASSERT(ZPL_ARRAY_HEADER(x)->count > 0); \
ZPL_ARRAY_HEADER(x)->count--; \
} while (0)
#define zpl_array_back(x) x[ZPL_ARRAY_HEADER(x)->count - 1]
#define zpl_array_front(x) x[0]
#define zpl_array_clear(x) \
do { ZPL_ARRAY_HEADER(x)->count = 0; } while (0)
#define zpl_array_resize(x, new_count) \
do { \
if (ZPL_ARRAY_HEADER(x)->capacity < (new_count)) zpl_array_grow(x, (new_count)); \
ZPL_ARRAY_HEADER(x)->count = (new_count); \
} while (0)
#define zpl_array_reserve(x, new_capacity) \
do { \
if (ZPL_ARRAY_HEADER(x)->capacity < (new_capacity)) zpl_array_set_capacity(x, new_capacity); \
} while (0)
ZPL_END_C_DECLS

View file

@ -0,0 +1,81 @@
// file: header/core/collections/buffer.h
////////////////////////////////////////////////////////////////
//
// Fixed Capacity Buffer (POD Types)
//
//
// zpl_buffer(Type) works like zpl_string or zpl_array where the actual type is just a pointer to the first
// element.
//
// Available Procedures for zpl_buffer(Type)
// zpl_buffer_init
// zpl_buffer_free
// zpl_buffer_append
// zpl_buffer_appendv
// zpl_buffer_pop
// zpl_buffer_clear
#ifdef ZPL_EDITOR
#include <zpl.h>
#endif
ZPL_BEGIN_C_DECLS
typedef struct zpl_buffer_header {
zpl_allocator backing;
zpl_isize count;
zpl_isize capacity;
} zpl_buffer_header;
#define zpl_buffer(Type) Type *
#define zpl_buffer_make(Type, Name, allocator, cap) Type *Name; zpl_buffer_init(Name, allocator, cap)
#define ZPL_BUFFER_HEADER(x) (cast(zpl_buffer_header *)(x) - 1)
#define zpl_buffer_count(x) (ZPL_BUFFER_HEADER(x)->count)
#define zpl_buffer_capacity(x) (ZPL_BUFFER_HEADER(x)->capacity)
#define zpl_buffer_end(x) (x + (zpl_buffer_count(x) - 1))
#define zpl_buffer_init(x, allocator, cap) \
do { \
void **nx = cast(void **) & (x); \
zpl_buffer_header *zpl__bh = \
cast(zpl_buffer_header *) zpl_alloc((allocator), sizeof(zpl_buffer_header) + (cap)*zpl_size_of(*(x))); \
zpl__bh->backing = allocator; \
zpl__bh->count = 0; \
zpl__bh->capacity = cap; \
*nx = cast(void *)(zpl__bh + 1); \
} while (0)
// DEPRECATED(zpl_buffer_free): Use zpl_buffer_free2 instead
#define zpl_buffer_free(x, allocator) (zpl_free(allocator, ZPL_BUFFER_HEADER(x)))
#define zpl_buffer_free2(x) (zpl_free(ZPL_BUFFER_HEADER(x)->backing, ZPL_BUFFER_HEADER(x)))
#define zpl_buffer_append(x, item) \
do { (x)[zpl_buffer_count(x)++] = (item); } while (0)
#define zpl_buffer_appendv(x, items, item_count) \
do { \
ZPL_ASSERT(zpl_size_of(*(items)) == zpl_size_of(*(x))); \
ZPL_ASSERT(zpl_buffer_count(x) + item_count <= zpl_buffer_capacity(x)); \
zpl_memcopy(&(x)[zpl_buffer_count(x)], (items), zpl_size_of(*(x)) * (item_count)); \
zpl_buffer_count(x) += (item_count); \
} while (0)
#define zpl_buffer_copy_init(y, x) \
do { \
zpl_buffer_init_reserve(y, zpl_buffer_allocator(x), zpl_buffer_capacity(x)); \
zpl_memcopy(y, x, zpl_buffer_capacity(x) * zpl_size_of(*x)); \
zpl_buffer_count(y) = zpl_buffer_count(x); \
} while (0)
#define zpl_buffer_pop(x) \
do { \
ZPL_ASSERT(zpl_buffer_count(x) > 0); \
zpl_buffer_count(x)--; \
} while (0)
#define zpl_buffer_clear(x) \
do { zpl_buffer_count(x) = 0; } while (0)
ZPL_END_C_DECLS

View file

@ -0,0 +1,176 @@
// file: header/core/collections/hashtable.h
/** @file hashtable.c
@brief Instantiated hash table
@defgroup hashtable Instantiated hash table
@n
@n This is an attempt to implement a templated hash table
@n NOTE: The key is always a zpl_u64 for simplicity and you will _probably_ _never_ need anything bigger.
@n
@n Hash table type and function declaration, call: ZPL_TABLE_DECLARE(PREFIX, NAME, N, VALUE)
@n Hash table function definitions, call: ZPL_TABLE_DEFINE(NAME, N, VALUE)
@n
@n PREFIX - a prefix for function prototypes e.g. extern, static, etc.
@n NAME - Name of the Hash Table
@n FUNC - the name will prefix function names
@n VALUE - the type of the value to be stored
@n
@n tablename_init(NAME * h, zpl_allocator a);
@n tablename_destroy(NAME * h);
@n tablename_get(NAME * h, zpl_u64 key);
@n tablename_set(NAME * h, zpl_u64 key, VALUE value);
@n tablename_grow(NAME * h);
@n tablename_rehash(NAME * h, zpl_isize new_count);
@n tablename_remove(NAME * h, zpl_u64 key);
@{
*/
#ifdef ZPL_EDITOR
#include <zpl.h>
#endif
ZPL_BEGIN_C_DECLS
typedef struct zpl_hash_table_find_result {
zpl_isize hash_index;
zpl_isize entry_prev;
zpl_isize entry_index;
} zpl_hash_table_find_result;
#define ZPL_TABLE(PREFIX, NAME, FUNC, VALUE) \
ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE); \
ZPL_TABLE_DEFINE(NAME, FUNC, VALUE);
#define ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE) \
typedef struct ZPL_JOIN2(NAME, Entry) { \
zpl_u64 key; \
zpl_isize next; \
VALUE value; \
} ZPL_JOIN2(NAME, Entry); \
\
typedef struct NAME { \
zpl_array(zpl_isize) hashes; \
zpl_array(ZPL_JOIN2(NAME, Entry)) entries; \
} NAME; \
\
PREFIX void ZPL_JOIN2(FUNC, init)(NAME * h, zpl_allocator a); \
PREFIX void ZPL_JOIN2(FUNC, destroy)(NAME * h); \
PREFIX VALUE *ZPL_JOIN2(FUNC, get)(NAME * h, zpl_u64 key); \
PREFIX void ZPL_JOIN2(FUNC, set)(NAME * h, zpl_u64 key, VALUE value); \
PREFIX void ZPL_JOIN2(FUNC, grow)(NAME * h); \
PREFIX void ZPL_JOIN2(FUNC, rehash)(NAME * h, zpl_isize new_count); \
PREFIX void ZPL_JOIN2(FUNC, remove)(NAME * h, zpl_u64 key);
#define ZPL_TABLE_DEFINE(NAME, FUNC, VALUE) \
void ZPL_JOIN2(FUNC, init)(NAME * h, zpl_allocator a) { \
zpl_array_init(h->hashes, a); \
zpl_array_init(h->entries, a); \
} \
\
void ZPL_JOIN2(FUNC, destroy)(NAME * h) { \
if (h->entries) zpl_array_free(h->entries); \
if (h->hashes) zpl_array_free(h->hashes); \
} \
\
zpl_internal zpl_isize ZPL_JOIN2(FUNC, _add_entry)(NAME * h, zpl_u64 key) { \
zpl_isize index; \
ZPL_JOIN2(NAME, Entry) e = { 0 }; \
e.key = key; \
e.next = -1; \
index = zpl_array_count(h->entries); \
zpl_array_append(h->entries, e); \
return index; \
} \
\
zpl_internal zpl_hash_table_find_result ZPL_JOIN2(FUNC, _find)(NAME * h, zpl_u64 key) { \
zpl_hash_table_find_result r = { -1, -1, -1 }; \
if (zpl_array_count(h->hashes) > 0) { \
r.hash_index = key % zpl_array_count(h->hashes); \
r.entry_index = h->hashes[r.hash_index]; \
while (r.entry_index >= 0) { \
if (h->entries[r.entry_index].key == key) return r; \
r.entry_prev = r.entry_index; \
r.entry_index = h->entries[r.entry_index].next; \
} \
} \
return r; \
} \
\
zpl_internal zpl_b32 ZPL_JOIN2(FUNC, _full)(NAME * h) { \
return 0.75f * zpl_array_count(h->hashes) < zpl_array_count(h->entries); \
} \
\
void ZPL_JOIN2(FUNC, grow)(NAME * h) { \
zpl_isize new_count = ZPL_ARRAY_GROW_FORMULA(zpl_array_count(h->entries)); \
ZPL_JOIN2(FUNC, rehash)(h, new_count); \
} \
\
void ZPL_JOIN2(FUNC, rehash)(NAME * h, zpl_isize new_count) { \
zpl_isize i, j; \
NAME nh = { 0 }; \
ZPL_JOIN2(FUNC, init)(&nh, zpl_array_allocator(h->hashes)); \
zpl_array_resize(nh.hashes, new_count); \
zpl_array_reserve(nh.entries, zpl_array_count(h->entries)); \
for (i = 0; i < new_count; i++) nh.hashes[i] = -1; \
for (i = 0; i < zpl_array_count(h->entries); i++) { \
ZPL_JOIN2(NAME, Entry) * e; \
zpl_hash_table_find_result fr; \
if (zpl_array_count(nh.hashes) == 0) ZPL_JOIN2(FUNC, grow)(&nh); \
e = &h->entries[i]; \
fr = ZPL_JOIN2(FUNC, _find)(&nh, e->key); \
j = ZPL_JOIN2(FUNC, _add_entry)(&nh, e->key); \
if (fr.entry_prev < 0) \
nh.hashes[fr.hash_index] = j; \
else \
nh.entries[fr.entry_prev].next = j; \
nh.entries[j].next = fr.entry_index; \
nh.entries[j].value = e->value; \
} \
ZPL_JOIN2(FUNC, destroy)(h); \
h->hashes = nh.hashes; \
h->entries = nh.entries; \
} \
\
VALUE *ZPL_JOIN2(FUNC, get)(NAME * h, zpl_u64 key) { \
zpl_isize index = ZPL_JOIN2(FUNC, _find)(h, key).entry_index; \
if (index >= 0) return &h->entries[index].value; \
return NULL; \
} \
\
void ZPL_JOIN2(FUNC, remove)(NAME * h, zpl_u64 key) { \
zpl_hash_table_find_result fr = ZPL_JOIN2(FUNC, _find)(h, key); \
if (fr.entry_index >= 0) { \
if (fr.entry_prev >= 0) { \
h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next; \
} else { \
h->hashes[fr.hash_index] = fr.entry_index; \
} \
zpl_array_remove_at(h->entries, fr.entry_index); \
} \
ZPL_JOIN2(FUNC, rehash)(h, zpl_array_count(h->entries)); \
} \
\
void ZPL_JOIN2(FUNC, set)(NAME * h, zpl_u64 key, VALUE value) { \
zpl_isize index; \
zpl_hash_table_find_result fr; \
if (zpl_array_count(h->hashes) == 0) ZPL_JOIN2(FUNC, grow)(h); \
fr = ZPL_JOIN2(FUNC, _find)(h, key); \
if (fr.entry_index >= 0) { \
index = fr.entry_index; \
} else { \
index = ZPL_JOIN2(FUNC, _add_entry)(h, key); \
if (fr.entry_prev >= 0) { \
h->entries[fr.entry_prev].next = index; \
} else { \
h->hashes[fr.hash_index] = index; \
} \
} \
h->entries[index].value = value; \
if (ZPL_JOIN2(FUNC, _full)(h)) ZPL_JOIN2(FUNC, grow)(h); \
}\
//! @}
ZPL_END_C_DECLS

Some files were not shown because too many files have changed in this diff Show more