Harden UE Wingman request handling and numeric property conversion.

Switch the Wingman protocol to null-delimited JSON, rework the server's
socket buffering and send logic, and document the bugs found during the
review. Also refactor WingProperty's numeric setters into clearer helper
paths while preserving the existing conversion rules.
This commit is contained in:
2026-04-06 01:44:21 -04:00
parent 9c1f474170
commit 5206700067
8 changed files with 264 additions and 110 deletions

View File

@@ -46,52 +46,61 @@ bool FWingProperty::SetObject(UObject *Obj, WingOut Errors) const
return true;
}
bool FWingProperty::SetDoubleInternal(
FNumericProperty *NProp, void *Container, double D, WingOut Errors)
{
uint8 buffer[16];
NProp->SetFloatingPointPropertyValue(buffer, D);
if (!FMath::IsFinite(NProp->GetFloatingPointPropertyValue(buffer)))
{
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lf\n"),
*WingUtils::FormatName(NProp), *NProp->GetCPPType(), D);
return false;
}
NProp->SetValue_InContainer(Container, buffer);
return true;
}
bool FWingProperty::SetInt64Internal(
FNumericProperty *NProp, void *Container, int64 I, WingOut Errors)
{
uint8 buffer[16];
if ((I < 0) && IsUnsigned(NProp))
{
Errors.Printf(TEXT(
"ERROR: Cannot store signed %lld in unsigned property %s\n"),
I, *WingUtils::FormatName(NProp));
return false;
}
NProp->SetIntPropertyValue(buffer, I);
if (NProp->GetSignedIntPropertyValue(buffer) != I)
{
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"),
*WingUtils::FormatName(NProp), *NProp->GetCPPType(), I);
return false;
}
NProp->SetValue_InContainer(Container, buffer);
return true;
}
bool FWingProperty::SetDouble(double D, WingOut Errors) const
{
if (!CheckEditable(Errors)) return false;
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
if (NProp == nullptr)
{
PrintExpectsReceived(TEXT("double"), Errors);
return false;
}
if (NProp->IsFloatingPoint())
else if (NProp->IsFloatingPoint())
{
uint8 buffer[16];
NProp->SetFloatingPointPropertyValue(buffer, D);
if (!FMath::IsFinite(NProp->GetFloatingPointPropertyValue(buffer)))
{
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lf\n"),
*WingUtils::FormatName(Prop), *Prop->GetCPPType(), D);
return false;
}
Prop->SetValue_InContainer(Container, buffer);
return true;
return SetDoubleInternal(NProp, Container, D, Errors);
}
else
{
uint8 buffer[16];
if (FMath::Floor(D) != D)
{
PrintExpectsReceived(TEXT("double"), Errors);
return false;
}
if (FMath::Abs(D) > (double)((1LL)<<53))
{
Errors.Printf(TEXT("ERROR: To store very large numbers in '%s', do not pass them as double. Use string or int.\n"),
*WingUtils::FormatName(Prop));
return false;
}
int64 I = (int64)D;
NProp->SetIntPropertyValue(buffer, I);
if (NProp->GetSignedIntPropertyValue(buffer) != I)
{
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"),
*WingUtils::FormatName(Prop), *Prop->GetCPPType(), I);
return false;
}
NProp->SetValue_InContainer(Container, buffer);
return true;
int64 I;
if (!LosslessDoubleToInt64(D, I, Errors)) return false;
return SetInt64Internal(NProp, Container, I, Errors);
}
}
@@ -99,38 +108,20 @@ bool FWingProperty::SetInt64(int64 I, WingOut Errors) const
{
if (!CheckEditable(Errors)) return false;
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
if (NProp == nullptr)
{
PrintExpectsReceived(TEXT("int"), Errors);
return false;
}
if (NProp->IsFloatingPoint())
else if (NProp->IsFloatingPoint())
{
uint8 buffer[16];
double D = I;
NProp->SetFloatingPointPropertyValue(buffer, D);
int64 RT = (int64)NProp->GetFloatingPointPropertyValue(buffer);
if (RT != I)
{
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"),
*WingUtils::FormatName(Prop), *Prop->GetCPPType(), I);
return false;
}
Prop->SetValue_InContainer(Container, buffer);
return true;
double D;
if (!LosslessInt64ToDouble(I, D, Errors)) return false;
return SetDoubleInternal(NProp, Container, D, Errors);
}
else
{
uint8 buffer[16];
NProp->SetIntPropertyValue(buffer, I);
if (NProp->GetSignedIntPropertyValue(buffer) != I)
{
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"),
*WingUtils::FormatName(Prop), *Prop->GetCPPType(), I);
return false;
}
NProp->SetValue_InContainer(Container, buffer);
return true;
return SetInt64Internal(NProp, Container, I, Errors);
}
}
@@ -597,4 +588,43 @@ bool FWingProperty::CheckEditable(WingOut Errors) const
return false;
}
return true;
}
}
bool FWingProperty::IsUnsigned(FNumericProperty* Prop)
{
return
CastField<FByteProperty>(Prop) ||
CastField<FUInt16Property>(Prop) ||
CastField<FUInt32Property>(Prop) ||
CastField<FUInt64Property>(Prop);
}
bool FWingProperty::LosslessDoubleToInt64(double D, int64 &I, WingOut Errors)
{
if (FMath::Floor(D) != D)
{
Errors.Printf(TEXT(
"ERROR: Converting double %.4lf to integer would lose precision\n"), D);
return false;
}
if (FMath::Abs(D) > (double)((1LL)<<53))
{
Errors.Printf(TEXT(
"ERROR: Converting huge double %lf to integer would lose data.\n"), D);
return false;
}
I = (int64)D;
return true;
}
bool FWingProperty::LosslessInt64ToDouble(int64 I, double &D, WingOut Errors)
{
D = (double)I;
if ((int64)D != I)
{
Errors.Printf(TEXT(
"ERROR: Converting huge integer %lld to floating point would lose data.\n"), I);
return false;
}
return true;
}