Binding correctly to NPC

This commit is contained in:
Crom (Thibaut CHARLES) 2015-04-27 23:10:07 +02:00
parent 25163296c5
commit afcc468803
4 changed files with 262 additions and 19 deletions

View File

@ -5,18 +5,42 @@ import std.stdio;
import std.socket;
import std.conv;
import std.utf;
import std.string;
string receiveWait(Socket sock){
bool blocking = sock.blocking;
sock.blocking = false;
ubyte[] rawData;
ubyte[256] buf;
int recv;
while((recv = sock.receive(buf))>=0 || rawData.length==0){
if(recv>0){
rawData ~= buf[0..recv];
}
Thread.sleep(dur!"msecs"(10));
}
sock.blocking = blocking;
return (cast(char*)rawData.ptr).fromStringz.to!string;
}
void sendWait(Socket sock, string data){
bool blocking = sock.blocking;
sock.blocking = true;
sock.send(data.toUTF8);
sock.blocking = blocking;
}
void main() {
writeln("Start");
auto sock = new TcpSocket;
sock.blocking = 1;
sock.blocking = true;
sock.connect(new InternetAddress("127.0.0.1", 8080));
writeln("Connected");
sock.send(`{"action":"bind","target":"testnpc"}`.toUTF8);
writeln("Sent");
Thread.sleep(dur!"seconds"(1));
sock.send(`{"action":"getenv","target":"testnpc"}`.toUTF8);
writeln("Sent2");
Thread.sleep(dur!"seconds"(1));
sock.sendWait(`{"action":"bind","target":"testnpc"}`);
writeln(sock.receiveWait);
sock.shutdown(SocketShutdown.BOTH);
}

View File

@ -0,0 +1,193 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.ComponentModel;
using CLRScriptFramework;
using NWScript;
using NWScript.ManagedInterfaceLayer.NWScriptManagedInterface;
using NWEffect = NWScript.NWScriptEngineStructure0;
using NWEvent = NWScript.NWScriptEngineStructure1;
using NWLocation = NWScript.NWScriptEngineStructure2;
using NWTalent = NWScript.NWScriptEngineStructure3;
using NWItemProperty = NWScript.NWScriptEngineStructure4;
using Newtonsoft.Json;
namespace CLRScript
{
class Agent : CLRScriptBase{
public uint objref;
public Agent(uint obj){
objref = obj;
}
public float facing{
get{
return _facing;
}
set{
_facing = value;
AssignCommand(objref, ()=>{SetFacing(_facing, TRUE);});
}
}
public NWLocation loc{
get{
return _loc;
}
set{
_loc = value;
AssignCommand(objref, ()=>{ActionForceMoveToLocation(_loc, TRUE, 6f);});
//Link effect
RemoveSEFFromObject(objref, "nwai_beam_loc");
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectNWN2SpecialEffectFile("nwai_beam_loc", OBJECT_INVALID, GetPositionFromLocation(_loc)), objref, -1f);
}
}
//Detection
//=======================================================================
public bool hasWallFront(float distance=10f){
Vector3 pointFront = Add(GetPositionFromLocation(loc), Multiply(AngleToVector(facing), distance));
return LineOfSightVector(GetPosition(objref), pointFront)==1;
}
public bool isInCombat(){
return GetIsInCombat(objref)==1;
}
//Json containing
// {"valid":"true", "id":"366642", "name":"Goblin", "distance":"13.37", "difficulty":"13", "hp":"12"}
public dynamic getNearestEnemy(float distanceMax=15f){
uint nearestEnemy = GetNearestCreature(
CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
objref, 1,
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN_AND_HEARD,
CREATURE_TYPE_IS_ALIVE, CREATURE_ALIVE_TRUE);
float dist = GetDistanceBetween(nearestEnemy, objref);
return JsonConvert.DeserializeObject<dynamic>("{"
+ "\"valid\":\""+(GetIsObjectValid(nearestEnemy)==TRUE && dist<=distanceMax).ToString()+"\","
+ "\"id\":\""+(nearestEnemy).ToString()+"\","
+ "\"name\":\""+GetName(nearestEnemy)+"\""
+ "\"distance\":\""+(dist).ToString()+"\","
+ "\"difficulty\":\""+GetChallengeRating(nearestEnemy)+"\""
+ "\"hp\":\""+GetCurrentHitPoints(nearestEnemy)+"\""
+ "}");
}
//Json containing
// {"valid":"true", "id":"366642", "name":"Guethenoc", "distance":"13.37"}
public dynamic getNearestPlayer(float distanceMax=15f){
uint pc = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, objref, 1, -1,-1,-1,-1);
float dist = GetDistanceBetween(pc, objref);
return JsonConvert.DeserializeObject<dynamic>("{"
+ "\"valid\":\""+(GetIsObjectValid(pc)==TRUE && dist<=distanceMax).ToString()+"\","
+ "\"id\":\""+(pc).ToString()+"\","
+ "\"name\":\""+GetName(pc)+"\""
+ "\"distance\":\""+(dist).ToString()+"\","
+ "}");
}
//Actions
//=======================================================================
public bool moveForward(float distance=10f){
if(hasWallFront(distance))
return false;
//See NWN2DataLib.AreaSurfaceMesh.PathExists
Vector3 pointFront = Add(GetPositionFromLocation(loc), Multiply(AngleToVector(facing), distance));
loc = Location(GetAreaFromLocation(loc), pointFront, facing);
return true;
}
public bool attackNearest(float distanceMax=15f){
var nearest = getNearestEnemy(distanceMax);
if(!nearest.valid)
return false;
AssignCommand(objref, ()=>{ActionAttack(nearest.id, FALSE);});
return true;
}
public bool turn(float angleDeg){
facing+=angleDeg;
return true;
}
public bool say(string message){
AssignCommand(objref, ()=>{SpeakString(message, TALKVOLUME_TALK);});
return getNearestPlayer().valid;
}
public string callCommand(string command)
{
Regex regexMethod = new Regex(@"([a-zA-Z_]+?)\s*\((.*?)\)");
Regex regexParam = new Regex("(\".*?\"|[0-9\\.]+)");
string methodName;
List<string> unparsedParams = new List<string>();
MatchCollection matches = regexMethod.Matches(command);
if(matches.Count == 1){
//Match name
methodName = matches[0].Groups[1].ToString();
//March parameters
string methodParamList = matches[0].Groups[2].ToString();
MatchCollection paramMatches = regexParam.Matches(methodParamList);
foreach(var p in paramMatches){
unparsedParams.Add(p.ToString().Trim('"'));
}
}
else{
//Failed to parse command
return "";
}
MethodInfo method = this.GetType().GetMethod(methodName);
ParameterInfo[] methodParams = method.GetParameters();
if(methodParams.Length != unparsedParams.Count){
//Incorrect number of parameters
return "";
}
List<Object> parsedParams = new List<Object>();
for(int i=0 ; i<methodParams.Length ; i++){
//Handle defaults?
var typeConverter = TypeDescriptor.GetConverter(methodParams[i].ParameterType);
parsedParams.Add(typeConverter.ConvertFromString(unparsedParams[i]));
}
return method.Invoke(this, parsedParams.ToArray()).ToString();
}
private float _facing;
private NWLocation _loc;
private Vector3 Multiply(Vector3 v, float f) {
v.x*=f; v.y*=f; v.z*=f;
return v;
}
private Vector3 Add(Vector3 v, Vector3 w) {
v.x*=w.x; v.y*=w.y; v.z*=w.z;
return v;
}
}
}

View File

@ -74,23 +74,44 @@ namespace CLRScript
DelayCommand(0.5f, Heartbeat);
//Process queued requests
Debug("Proc cmds");
//Debug("Proc cmds");
while(sockBufferIn.Count>0){
Tuple<string, Socket> req = sockBufferIn.Dequeue();
var socket = req.Item2;
var json = JsonConvert.DeserializeObject<dynamic>(req.Item1);
//TODO: check if json correctly parsed
switch ((string)json.action) {
case "bind": {
Debug("Bind request to '"+json.target+"'");
if (!sockClients.ContainsKey(json.target)) {
sockClients.Add(json.target, req.Item2);
//Reply ok
try {
if (!sockClients.ContainsKey((string)json.target)) {
sockClients.Add((string)json.target, socket);
socket.Send(Encoding.UTF8.GetBytes(
"{\"action\":\"bindack\", \"result\":\"ok\"}"
));
//handle env
}
else {
socket.Send(Encoding.UTF8.GetBytes(
"{\"action\":\"bindack\", \"result\":\"failed\", \"message\":\"Socket already binded with this NPC\"}"
));
}
}
else {
//Reply Already binded
catch (Exception e) {
socket.Send(Encoding.UTF8.GetBytes(
"{\"action\":\"bindack\", \"result\":\"failed\", \"message\":\"Thrown exception: "+e.ToString().Replace("\"","\\\"")+"\"}"
));
}
}break;
}break;
case "decision": {
}break;
}
}
}
@ -114,25 +135,29 @@ namespace CLRScript
sockClient.Blocking = true;
while (sockClient.Connected) {
byte[] rawData = new byte[sockClient.Available];
if (sockClient.Available > 0) {
byte[] rawData = new byte[sockClient.Available];
sockClient.Receive(rawData, rawData.Length, 0);
string data = Encoding.UTF8.GetString(rawData);
//Debug("Queued "+data);
Debug("Queued "+data);
sockBufferIn.Enqueue(new Tuple<string,Socket>(data, sockClient));
}
else
Thread.Sleep(100);//TODO: Find another way than sleeping
Thread.Sleep(10);
}
//Remove stored bind
var sock = sockClients.First(kvp => kvp.Value==sockClient);
sockClients.Remove(sock.Key);
}
Socket sockServer;
Queue<Tuple<string,Socket>> sockBufferIn;//Request & client
Dictionary<string, Socket> sockClients;//PNJ tag => Client socket
private void Debug(string msg){

View File

@ -51,6 +51,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Agent.cs" />
<Compile Include="nwn2ai_onmoduleload.cs" />
<Compile Include="..\CLRScriptSDK\NWScriptActions.cs" />
<Compile Include="..\CLRScriptSDK\NWScriptConstants.cs" />