progsig: Fix remove and clone program functions being completely broken.

Remove function only removed instructions from the local machine, and
was therefore not MP safe.
Clone function failed to work correctly for non-trivial cases,
and sometimes caused an array out of bounds assertion.

These are replaced by a new commandproc which does each operation
as a single action, which is therefore MP safe.

Remove an unused struct field.
This commit is contained in:
Jonathan G Rennison
2015-11-24 01:03:09 +00:00
parent 8760187899
commit 769b8ae096
5 changed files with 142 additions and 70 deletions

View File

@@ -201,6 +201,7 @@ CommandProc CmdOpenCloseAirport;
CommandProc CmdInsertSignalInstruction; CommandProc CmdInsertSignalInstruction;
CommandProc CmdModifySignalInstruction; CommandProc CmdModifySignalInstruction;
CommandProc CmdRemoveSignalInstruction; CommandProc CmdRemoveSignalInstruction;
CommandProc CmdSignalProgramMgmt;
#define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} #define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type}
@@ -362,6 +363,7 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdInsertSignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_INSERT_SIGNAL_INSTRUCTION DEF_CMD(CmdInsertSignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_INSERT_SIGNAL_INSTRUCTION
DEF_CMD(CmdModifySignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_MODIFY_SIGNAL_INSTRUCTION DEF_CMD(CmdModifySignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_MODIFY_SIGNAL_INSTRUCTION
DEF_CMD(CmdRemoveSignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SIGNAL_INSTRUCTION DEF_CMD(CmdRemoveSignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SIGNAL_INSTRUCTION
DEF_CMD(CmdSignalProgramMgmt, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_SIGNAL_PROGRAM_MGMT
}; };
/*! /*!

View File

@@ -332,6 +332,7 @@ enum Commands {
CMD_INSERT_SIGNAL_INSTRUCTION, ///< insert a signal instruction CMD_INSERT_SIGNAL_INSTRUCTION, ///< insert a signal instruction
CMD_MODIFY_SIGNAL_INSTRUCTION, ///< modifies a signal instruction CMD_MODIFY_SIGNAL_INSTRUCTION, ///< modifies a signal instruction
CMD_REMOVE_SIGNAL_INSTRUCTION, ///< removes a signal instruction CMD_REMOVE_SIGNAL_INSTRUCTION, ///< removes a signal instruction
CMD_SIGNAL_PROGRAM_MGMT, ///< removes a signal program management command
CMD_END, ///< Must ALWAYS be on the end of this list!! (period) CMD_END, ///< Must ALWAYS be on the end of this list!! (period)
}; };

View File

@@ -476,7 +476,7 @@ CommandCost CmdInsertSignalInstruction(TileIndex tile, DoCommandFlag flags, uint
SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track)); SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
if (!prog) if (!prog)
return_cmd_error(STR_ERR_PROGSIG_NOT_THERE); return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
if (instruction_id > prog->instructions.Length()) if (instruction_id >= prog->instructions.Length())
return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION); return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
bool exec = (flags & DC_EXEC) != 0; bool exec = (flags & DC_EXEC) != 0;
@@ -700,3 +700,130 @@ CommandCost CmdRemoveSignalInstruction(TileIndex tile, DoCommandFlag flags, uint
InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track); InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
return CommandCost(); return CommandCost();
} }
static void CloneInstructions(SignalProgram *prog, SignalInstruction *insert_before, SignalInstruction *si)
{
while(true) {
if(si == NULL) break;
switch(si->Opcode()) {
case PSO_SET_SIGNAL: {
SignalSet *set = new SignalSet(prog, ((SignalSet*)si)->to_state);
set->Insert(insert_before);
si = ((SignalSet*)si)->next;
break;
}
case PSO_IF: {
SignalIf *if_ins = new SignalIf(prog);
if_ins->Insert(insert_before);
CloneInstructions(prog, if_ins->if_true, ((SignalIf*)si)->if_true);
CloneInstructions(prog, if_ins->if_false, ((SignalIf*)si)->if_false);
SignalCondition *src_cond = ((SignalIf *) si)->condition;
SignalConditionCode code = src_cond->ConditionCode();
switch (code) {
case PSC_ALWAYS:
case PSC_NEVER:
if_ins->SetCondition(new SignalSimpleCondition(code));
break;
case PSC_NUM_GREEN:
case PSC_NUM_RED: {
SignalVariableCondition *cond = new SignalVariableCondition(code);
cond->comparator = ((SignalVariableCondition *) src_cond)->comparator;
cond->value = ((SignalVariableCondition *) src_cond)->value;
if_ins->SetCondition(cond);
break;
}
case PSC_SIGNAL_STATE: {
SignalStateCondition *src = ((SignalStateCondition *) src_cond);
if_ins->SetCondition(new SignalStateCondition(SignalReference(prog->tile, prog->track), src->sig_tile, src->sig_track));
break;
}
default: NOT_REACHED();
}
si = ((SignalIf*)si)->after;
break;
}
case PSO_LAST:
case PSO_IF_ELSE:
case PSO_IF_ENDIF:
return;
default:
NOT_REACHED();
}
}
}
/** Insert a signal instruction into the signal program.
*
* @param tile The Tile on which to perform the operation
* @param p1 Flags and information
* - Bits 0-2 Which track the signal sits on
* - Bits 3-6 Management code
* For clone action:
* - Bits 7-9 Which track the clone source signal sits on
* @param p2
* For clone action:
* - Tile of clone source signal
* @param text unused
*/
CommandCost CmdSignalProgramMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
bool exec = (flags & DC_EXEC) != 0;
Track track = Extract<Track, 0, 3>(p1);
SignalProgramMgmtCode mgmt = static_cast<SignalProgramMgmtCode>(GB(p1, 3, 4));
if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
return CMD_ERROR;
}
if (!IsTileOwner(tile, _current_company)) return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
if (!prog) return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
switch (mgmt) {
case SPMC_REMOVE:
if (exec) {
prog->first_instruction->Remove();
}
break;
case SPMC_CLONE: {
TileIndex src_tile = p2;
Track src_track = Extract<Track, 7, 3>(p1);
if (!IsValidTrack(src_track) || !IsPlainRailTile(src_tile) || !HasTrack(src_tile, src_track)) {
return CMD_ERROR;
}
if (!IsTileOwner(src_tile, _current_company)) return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
SignalProgram *src_prog = GetExistingSignalProgram(SignalReference(src_tile, src_track));
if (!src_prog) return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
if (exec) {
prog->first_instruction->Remove();
CloneInstructions(prog, prog->last_instruction, ((SignalSpecial*) src_prog->first_instruction)->next);
}
break;
}
default:
return CMD_ERROR;
}
if (exec) {
AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
UpdateSignalsInBuffer();
InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
}
return CommandCost();
}

View File

@@ -28,6 +28,11 @@ class SignalInstruction;
class SignalSpecial; class SignalSpecial;
typedef SmallVector<SignalInstruction*, 4> InstructionList; typedef SmallVector<SignalInstruction*, 4> InstructionList;
enum SignalProgramMgmtCode {
SPMC_REMOVE, ///< Remove program
SPMC_CLONE, ///< Clone program
};
/** The actual programmable signal information */ /** The actual programmable signal information */
struct SignalProgram { struct SignalProgram {
SignalProgram(TileIndex tile, Track track, bool raw = false); SignalProgram(TileIndex tile, Track track, bool raw = false);
@@ -212,7 +217,6 @@ class SignalStateCondition: public SignalCondition {
SignalReference this_sig; SignalReference this_sig;
TileIndex sig_tile; TileIndex sig_tile;
Trackdir sig_track; Trackdir sig_track;
SignalState state;
}; };
// -- Instructions // -- Instructions

View File

@@ -256,8 +256,7 @@ public:
case PROGRAM_WIDGET_REMOVE: { case PROGRAM_WIDGET_REMOVE: {
SignalInstruction *ins = GetSelected(); SignalInstruction *ins = GetSelected();
if (this->GetOwner() != _local_company || !ins) if (ins == NULL) return;
return;
uint32 p1 = 0; uint32 p1 = 0;
SB(p1, 0, 3, this->track); SB(p1, 0, 3, this->track);
@@ -336,15 +335,13 @@ public:
ScrollMainWindowToTile(this->tile); ScrollMainWindowToTile(this->tile);
// this->RaiseWidget(PROGRAM_WIDGET_GOTO_SIGNAL); // this->RaiseWidget(PROGRAM_WIDGET_GOTO_SIGNAL);
} break; } break;
case PROGRAM_WIDGET_REMOVE_PROGRAM: {
if (this->GetOwner() != _local_company)
return;
program->first_instruction->Remove();
case PROGRAM_WIDGET_REMOVE_PROGRAM: {
DoCommandP(this->tile, this->track | (SPMC_REMOVE << 3), 0, CMD_SIGNAL_PROGRAM_MGMT | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
this->RebuildInstructionList(); this->RebuildInstructionList();
} break; } break;
case PROGRAM_WIDGET_COPY_PROGRAM: {
case PROGRAM_WIDGET_COPY_PROGRAM: {
this->ToggleWidgetLoweredState(PROGRAM_WIDGET_COPY_PROGRAM); this->ToggleWidgetLoweredState(PROGRAM_WIDGET_COPY_PROGRAM);
this->SetWidgetDirty(PROGRAM_WIDGET_COPY_PROGRAM); this->SetWidgetDirty(PROGRAM_WIDGET_COPY_PROGRAM);
if (this->IsWidgetLowered(PROGRAM_WIDGET_COPY_PROGRAM)) { if (this->IsWidgetLowered(PROGRAM_WIDGET_COPY_PROGRAM)) {
@@ -356,62 +353,6 @@ public:
} }
} }
void InsertInstruction(SignalInstruction *si, uint32 next)
{
uint64 p1 = 0;
while(true) {
if(si == NULL) break;
switch(si->Opcode()) {
case PSO_SET_SIGNAL: {
SB(p1, 0, 3, this->track);
SB(p1, 3, 16, next);
SB(p1, 19, 8, si->Opcode());
DoCommandP(this->tile, p1, 0, CMD_INSERT_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
this->RebuildInstructionList();
si = ((SignalSet*)si)->next;
} break;
case PSO_IF: {
SB(p1, 0, 3, this->track);
SB(p1, 3, 16, next);
SB(p1, 19, 8, si->Opcode());
DoCommandP(this->tile, p1, 0, CMD_INSERT_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
this->RebuildInstructionList();
SignalInstruction *s = ((SignalIf*)si)->if_true;
while(s->Opcode() != PSO_IF_ELSE) {
if(s->Opcode() == PSO_IF) s = ((SignalIf*)s)->after;
if(s->Opcode() == PSO_SET_SIGNAL) s = ((SignalSet*)s)->next;
else break;
}
InsertInstruction(((SignalIf*)si)->if_true, s->Id());
this->RebuildInstructionList();
s = ((SignalIf*)si)->if_false;
while(s->Opcode() != PSO_IF_ENDIF) {
if(s->Opcode() == PSO_IF) s = ((SignalIf*)s)->after;
if(s->Opcode() == PSO_SET_SIGNAL) s = ((SignalSet*)s)->next;
else break;
}
InsertInstruction(((SignalIf*)si)->if_false, s->Id());
this->RebuildInstructionList();
si = ((SignalIf*)si)->after;
} break;
case PSO_LAST:
case PSO_IF_ELSE:
case PSO_IF_ENDIF:
return;
default:
NOT_REACHED();
}
}
}
virtual void OnPlaceObject(Point pt, TileIndex tile1) OVERRIDE virtual void OnPlaceObject(Point pt, TileIndex tile1) OVERRIDE
{ {
if (this->IsWidgetLowered(PROGRAM_WIDGET_COPY_PROGRAM)) { if (this->IsWidgetLowered(PROGRAM_WIDGET_COPY_PROGRAM)) {
@@ -446,13 +387,10 @@ public:
ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_NOT_AN_EXIT_SIGNAL, WL_INFO); ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_NOT_AN_EXIT_SIGNAL, WL_INFO);
return; return;
} }
program->first_instruction->Remove(); DoCommandP(this->tile, this->track | (SPMC_CLONE << 3) | (track1 << 7), tile1, CMD_SIGNAL_PROGRAM_MGMT | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
this->RebuildInstructionList();
SignalInstruction *si = ((SignalSpecial*)sp->first_instruction)->next;
InsertInstruction(si, program->last_instruction->Id());
ResetObjectToPlace(); ResetObjectToPlace();
this->RaiseWidget(PROGRAM_WIDGET_COPY_PROGRAM); this->RaiseWidget(PROGRAM_WIDGET_COPY_PROGRAM);
this->RebuildInstructionList();
//OnPaint(); // this appears to cause visual artefacts //OnPaint(); // this appears to cause visual artefacts
return; return;
} }